//
//  XTTableInfo.m
//  XTads
//
//  Created by Rune Berg on 30/10/2018.
//  Copyright © 2018 Rune Berg. All rights reserved.
//

#import "XTTableInfo.h"
#import "XTTableColumnInfo.h"
#import "XTTextTable.h"
#import "XTTextTableBlock.h"
#import "XTLogger.h"
#import "XTAllocDeallocCounter.h"


@interface XTTableInfo ()

@property NSMutableDictionary<NSNumber*, XTTableColumnInfo*> *columnInfoByColumn;
@property CGFloat columnWidthRemainder;

@end


@implementation XTTableInfo

static XTLogger *logger;

+ (void)initialize
{
	if (self == [XTTableInfo class]) {
		logger = [XTLogger loggerForClass:[XTTableInfo class]];
	}
}

OVERRIDE_ALLOC_FOR_COUNTER
OVERRIDE_DEALLOC_FOR_COUNTER

- (instancetype)init
{
	self = [super init];
	if (self) {
		_columnInfoByColumn = [NSMutableDictionary dictionaryWithCapacity:10];
		_columnWidthRemainder = 0.0;
	}
	return self;
}

- (void)noteMaxContentRectWidth:(CGFloat)width
					  forColumn:(NSInteger)column
{
	XTTableColumnInfo *columnInfo = [self getOrCreateColumnInfo:column];

	NSNumber *maxWidthObj = columnInfo.maxContentRectWidth;
	if (maxWidthObj == nil || width > maxWidthObj.doubleValue) {
		//XT_DEF_SELNAME;
		//XT_WARN_2(@"new maxWidth %lf for col. %d", width, column);
		columnInfo.maxContentRectWidth = [NSNumber numberWithDouble:width];
	}
}

- (void)noteMinContentRectWidth:(CGFloat)width
					  forColumn:(NSInteger)column
{
	XTTableColumnInfo *columnInfo = [self getOrCreateColumnInfo:column];
	
	NSNumber *minWidthObj = columnInfo.minContentRectWidth;
	if (minWidthObj == nil || width > minWidthObj.doubleValue) { // it's the "max min" value we're tracking here
		//XT_DEF_SELNAME;
		//XT_WARN_2(@"new minWidth %lf for col. %d", width, column);
		columnInfo.minContentRectWidth = [NSNumber numberWithDouble:width];
	}
}

- (void)noteTotalBoundsWidthForTextTableBlock:(XTTextTableBlock *)textTableBlock
										width:(CGFloat)width
{
	//XT_DEF_SELNAME;
	
	NSInteger column = textTableBlock.startingColumn;
	XTTableColumnInfo *columnInfo = [self getOrCreateColumnInfo:column];

	columnInfo.totalBoundsWidth = [NSNumber numberWithDouble:width];

	//XT_WARN_2(@"col %ld : %ld", column, (NSInteger)width);
}

- (void)noteBoundsRectWidthForTextTableBlock:(XTTextTableBlock *)textTableBlock
									   width:(CGFloat)width
{
	//XT_DEF_SELNAME;
	
	NSInteger column = textTableBlock.startingColumn;
	XTTableColumnInfo *columnInfo = [self getOrCreateColumnInfo:column];
	
	columnInfo.boundsRectWidth = [NSNumber numberWithDouble:width];

	//XT_WARN_2(@"col %ld : %ld", column, (NSInteger)width);
}

- (CGFloat)contectRectWidthForTextTableBlock:(XTTextTableBlock *)textTableBlock
								 usableWidth:(CGFloat)usableWidth
{
	//XT_DEF_SELNAME;

	NSInteger column = textTableBlock.startingColumn;
	NSInteger row = textTableBlock.startingRow;
	//XT_WARN_3(@"col=%ld row=%ld usableWidth=%lf", column, row, usableWidth);

	XTTextTable *table = (XTTextTable *)textTableBlock.table;
	
	// Recalc all columns widths, so that x pos calcs can be done right:
	NSUInteger columnCount = self.columnInfoByColumn.count;
	CGFloat contectRectWidth;
	for (NSUInteger iterColumn = 0; iterColumn < columnCount; iterColumn++) {
		CGFloat tempContectRectWidth = [self contectRectWidthForTextTable:table row:row column:iterColumn usableWidth:usableWidth];
		if (iterColumn == column) {
			contectRectWidth = tempContectRectWidth;
		}
	}

	return contectRectWidth;
}

- (CGFloat)contectRectWidthForTextTable:(XTTextTable *)table
									row:(NSInteger)row
								 column:(NSInteger)column
							usableWidth:(CGFloat)usableWidth
{
	//XT_DEF_SELNAME;
	
	NSNumber *columnObj = [NSNumber numberWithInteger:column];
	XTTableColumnInfo *columnInfo = self.columnInfoByColumn[columnObj];
	
	//XT_WARN_3(@"col=%ld row=%ld usableWidth=%lf", column, row, usableWidth);
	
	CGFloat contectRectWidth;
	CGFloat totalMinColumnWidthIncludingBounds = [self totalMinColumnWidthIncludingBounds];
	
	if (column == 0) {
		self.columnWidthRemainder = 0.0;
	}
	
	BOOL usingTableWidthPercentage = NO;
	BOOL usingTableWidthPoints = NO;
	CGFloat usableWidthScaledToTableWidthPercentage = usableWidth;
	
	if (table.widthAsPoints != nil) {
		CGFloat widthAsPoints = (CGFloat)table.widthAsPoints.integerValue;
		if (widthAsPoints < usableWidth) {
			if (widthAsPoints >= totalMinColumnWidthIncludingBounds) {
				usableWidth = widthAsPoints;
				//XT_WARN_1(@"widthAsPoints >= totalMinColumnWidthIncludingBounds --> usableWidth = widthAsPoints : %lf", usableWidth);
				usingTableWidthPoints = YES;
			} else if (totalMinColumnWidthIncludingBounds < usableWidth) {
				usableWidth = totalMinColumnWidthIncludingBounds;
				//XT_WARN_1(@"totalMinColumnWidthIncludingBounds < usableWidth --> usableWidth = widthAsPoints : %lf", usableWidth);
				usingTableWidthPoints = YES;
			} else {
				//XT_WARN_1(@"else: TODO? --> usableWidth unchanged: %lf", usableWidth);
				usingTableWidthPoints = YES;
				// _2e, _2g get us here
			}
		} else {
			//XT_WARN_1(@"else#2: TODO? --> usableWidth unchanged: %lf", usableWidth);
			usingTableWidthPoints = YES;
		}
	} else {
		if (table.widthAsPercentage != nil) {
			//TODO !!! can we handle % case as simply as pts case? if so, make this block a sep meth
			CGFloat widthAsPercentage = (CGFloat)table.widthAsPercentage.integerValue;
			usableWidthScaledToTableWidthPercentage = (usableWidth * widthAsPercentage) / 100.0;
			//XT_WARN_1(@"usableWidthScaledToTableWidthPercentage = %lf", usableWidthScaledToTableWidthPercentage);
			
			/*
			if (usableWidthScaledToTableWidthPercentage != floor(usableWidthScaledToTableWidthPercentage) ||
				usableWidthScaledToTableWidthPercentage != ceil(usableWidthScaledToTableWidthPercentage)) {
				int brkpt = 1;
			}*/
			
			//TODO !!! handle <0 & >100
			usingTableWidthPercentage = YES;
		}
	}
	
	if (totalMinColumnWidthIncludingBounds > usableWidth) {
		// Not width enough for even the minimal width of each column
		
		//XT_WARN_2(@"row col %ld %ld : totalMinColumnWidthIncludingBounds > usableWidth", row, column);
		contectRectWidth = [self getForcefullyTruncatedContentRectWidthForColumn:column
																	 usableWidth:usableWidth];
		
	} else if ([self totalMaxColumnWidthIncludingBounds] < usableWidth) {
		// Width enough for every column to have all they want
		
		//XT_WARN_2(@"row col %ld %ld : [self totalMaxColumnWidthIncludingBounds] < usableWidth", row, column);
		contectRectWidth = columnInfo.maxContentRectWidth.doubleValue;
		
		if (usingTableWidthPercentage) {
			if (totalMinColumnWidthIncludingBounds < usableWidthScaledToTableWidthPercentage) {
				//CGFloat origContectRectWidth = contectRectWidth;
				contectRectWidth = [self adjustTableWidthPercentageContentRectWidth3:contectRectWidth
																		forTextTable:table
																			  column:column
																		 usableWidth:usableWidth];
				//XT_WARN_2(@"adjust to %% : %lf -> %lf", origContectRectWidth, contectRectWidth);
			} else {
				//XT_WARN_0(@"adjust to minCRW");
				contectRectWidth = columnInfo.minContentRectWidth.doubleValue;
			}
		} else if (usingTableWidthPoints) {
			contectRectWidth = [self adjustTableWidthPointsContentRectWidth:contectRectWidth
																  forColumn:column
																usableWidth:usableWidth];
		}
		
	} else if (columnInfo.minContentRectWidth.doubleValue == columnInfo.maxContentRectWidth.doubleValue) {
		
		//XT_WARN_2(@"row col %ld %ld : columnInfo.minContentRectWidth == columnInfo.maxContentRectWidth", row, column);
		contectRectWidth = columnInfo.minContentRectWidth.doubleValue;
		
		if (usingTableWidthPercentage) {
			if (totalMinColumnWidthIncludingBounds < usableWidthScaledToTableWidthPercentage) {
				//CGFloat origContectRectWidth = contectRectWidth;
				contectRectWidth = [self adjustTableWidthPercentageContentRectWidth2:contectRectWidth
																		forTextTable:table
																		 usableWidth:usableWidth];
				//XT_WARN_2(@"adjust to %% : %lf -> %lf", origContectRectWidth, contectRectWidth);
			} else {
				//XT_WARN_0(@"adjust to minCRW");
				//contectRectWidth = columnInfo.minContentRectWidth.doubleValue;
			}
		} else if (usingTableWidthPoints) {
			contectRectWidth = [self adjustTableWidthPointsContentRectWidth:contectRectWidth
																  forColumn:column
																usableWidth:usableWidth];
		}
		
	} else {
		//XT_WARN_2(@"row col %ld %ld : else/other", row, column);
		contectRectWidth = [self getContentRectWidthForColumn:column
												  usableWidth:usableWidth];
		
		if (usingTableWidthPercentage) {
			if (totalMinColumnWidthIncludingBounds < usableWidthScaledToTableWidthPercentage) {
				//CGFloat origContectRectWidth = contectRectWidth;
				contectRectWidth = [self adjustTableWidthPercentageContentRectWidth:contectRectWidth
																	   forTextTable:table
																		usableWidth:usableWidth];
					//TODO !!! makes contectRectWidth negative for xtads_tabtag_tables_4a.t, first column
				//XT_WARN_2(@"adjust to %% : %lf -> %lf", origContectRectWidth, contectRectWidth);
			} else {
				//XT_WARN_0(@"adjust to minCRW");
				contectRectWidth = columnInfo.minContentRectWidth.doubleValue;
			}
		} else if (usingTableWidthPoints) {
			int brkpt = 1;
		}
	}
	
	self.columnWidthRemainder += contectRectWidth - floor(contectRectWidth);
	CGFloat columnWidthRemainderToAdd = 0.0;
	if (self.columnWidthRemainder >= 1.0) {
		columnWidthRemainderToAdd = floor(self.columnWidthRemainder);
		self.columnWidthRemainder -= columnWidthRemainderToAdd;
	}
	contectRectWidth = floor(contectRectWidth);
	contectRectWidth += columnWidthRemainderToAdd;
	
	columnInfo.contentRectWidth = [NSNumber numberWithDouble:contectRectWidth];
	
	if (contectRectWidth < 0.0) {
		XT_DEF_SELNAME;
		XT_WARN_1(@"contectRectWidth=%lf < 0.0", contectRectWidth);
	}
	//XT_WARN_4(@"row col %ld %ld --> %lf (%lf)", row, column, contectRectWidth, [self totalWidthOfBounds]);
	return contectRectWidth;
}

- (CGFloat)adjustTableWidthPercentageContentRectWidth:(CGFloat)contentRectWidth
										 forTextTable:(XTTextTable *)table
										  usableWidth:(CGFloat)usableWidth
{
	//XT_DEF_SELNAME;

	if (table.widthAsPercentage != nil) {
		CGFloat widthAsPercentage = ((CGFloat)table.widthAsPercentage.integerValue);
		if (widthAsPercentage >= 1.0) {
			if (widthAsPercentage > 100.0) {
				widthAsPercentage = 100.0;
			}
			CGFloat usableWidthScaledToPct = (usableWidth * widthAsPercentage) / 100.0;
			CGFloat widthToDistribute = usableWidthScaledToPct - usableWidth;
			CGFloat columnCount = self.columnInfoByColumn.count;
			CGFloat widthCorrection = widthToDistribute / columnCount;
			contentRectWidth += widthCorrection;
		}
	}
	
	//XT_WARN_3(@"row=%ld col=%ld --> %lf", textTableBlock.startingRow, textTableBlock.startingColumn, contentRectWidth);
	//XT_WARN_1(@" --> %lf", contentRectWidth);
	return contentRectWidth;
}

- (CGFloat)adjustTableWidthPercentageContentRectWidth2:(CGFloat)contentRectWidth
										  forTextTable:(XTTextTable *)table
										   usableWidth:(CGFloat)usableWidth
{
	//XT_DEF_SELNAME;
	//XT_WARN_2(@"row=%ld col=%ld", textTableBlock.startingRow, textTableBlock.startingColumn);

	CGFloat res = contentRectWidth;
	
	if (table.widthAsPercentage != nil) {
		CGFloat widthAsPercentage = ((CGFloat)table.widthAsPercentage.integerValue);
		if (widthAsPercentage >= 1.0) {
			if (widthAsPercentage > 100.0) {
				widthAsPercentage = 100.0;
			}
			CGFloat totalMinColumnWidthIncludingBounds = [self totalMinColumnWidthIncludingBounds];
			//XT_WARN_1(@"totalMinColumnWidthIncludingBounds=%lf", totalMinColumnWidthIncludingBounds);
			CGFloat usableWidthScaledToPct = (usableWidth * widthAsPercentage) / 100.0;
			CGFloat widthToDistribute = usableWidthScaledToPct - totalMinColumnWidthIncludingBounds;
			CGFloat columnCount = self.columnInfoByColumn.count;
			CGFloat widthCorrection = widthToDistribute / columnCount;
			if (widthCorrection < 0.0) {
				XT_DEF_SELNAME;
				XT_WARN_1(@"widthCorrection %lf < 0.0", widthCorrection);
			}
			res += widthCorrection;
			if (res < 0.0) {
				XT_DEF_SELNAME;
				XT_WARN_1(@"res=%lf < 0.0", res);
			}
		}
	}

	//XT_WARN_3(@"row=%ld col=%ld --> %lf", textTableBlock.startingRow, textTableBlock.startingColumn, contentRectWidth);
	//XT_WARN_1(@"--> %lf", res);
	return res;;
}

- (CGFloat)adjustTableWidthPercentageContentRectWidth3:(CGFloat)contentRectWidth
										  forTextTable:(XTTextTable *)table
												column:(NSInteger)column
										   usableWidth:(CGFloat)usableWidth
{
	//XT_DEF_SELNAME;
	//XT_WARN_3(@"col=%ld contentRectWidth=%lf usableWidth=%lf", column, contentRectWidth, usableWidth);
	
	CGFloat res = contentRectWidth;
	
	if (table.widthAsPercentage != nil) {
		CGFloat widthAsPercentage = ((CGFloat)table.widthAsPercentage.integerValue);
		if (widthAsPercentage >= 1.0) {
			XTTableColumnInfo *columnInfo = [self getColumnInfo:column];

			if (widthAsPercentage > 100.0) {
				widthAsPercentage = 100.0;
			}
			CGFloat totalMinColumnWidthIncludingBounds = [self totalMinColumnWidthIncludingBounds];
			CGFloat totalMaxColumnWidthIncludingBounds = [self totalMaxColumnWidthIncludingBounds];
			CGFloat totalMaxColumnWidthExcludingBounds = [self totalMaxColumnWidthExcludingBounds];
			//XT_WARN_2(@"totalMinColumnWidthIncludingBounds=%lf totalMaxColumnWidthExcludingBounds=%lf", totalMinColumnWidthIncludingBounds, totalMaxColumnWidthExcludingBounds);
			CGFloat usableWidthScaledToPct = (usableWidth * widthAsPercentage) / 100.0;
			
			CGFloat toSubtract;
			if (totalMaxColumnWidthIncludingBounds < usableWidthScaledToPct) {
				res = columnInfo.maxContentRectWidth.floatValue;
				toSubtract = totalMaxColumnWidthIncludingBounds;
				//XT_WARN_0(@"totalMaxColumnWidthIncludingBounds < usableWidthScaledToPct");
			} else {
				res = columnInfo.minContentRectWidth.floatValue;
				toSubtract = totalMinColumnWidthIncludingBounds;
				//XT_WARN_0(@"totalMaxColumnWidthIncludingBounds >= usableWidthScaledToPct");
			}
			
			CGFloat widthToDistribute2 = usableWidthScaledToPct - toSubtract;
			//XT_WARN_2(@"usableWidthScaledToPct=%lf widthToDistribute2=%lf", usableWidthScaledToPct, widthToDistribute2);
			CGFloat fractionExtraForThisColumn = columnInfo.maxContentRectWidth.floatValue / totalMaxColumnWidthExcludingBounds;
			CGFloat extraForThisColumn = widthToDistribute2 * fractionExtraForThisColumn;
			//XT_WARN_2(@"fractionExtraForThisColumn=%lf extraForThisColumn=%lf", fractionExtraForThisColumn, extraForThisColumn);
			if (extraForThisColumn < 0.0) {
				XT_DEF_SELNAME;
				XT_WARN_2(@"col=%ld extraForThisColumn %lf < 0.0", column, extraForThisColumn);
			}
			res += extraForThisColumn;
			if (res < 0.0) {
				XT_DEF_SELNAME;
				XT_WARN_2(@"col=%ld res %lf < 0.0", column, res);
			}
		}
	}
	
	//XT_WARN_2(@"col=%ld --> %lf", column, res);
	return res;;
}

- (CGFloat)adjustTableWidthPointsContentRectWidth:(CGFloat)contectRectWidth
										forColumn:(NSInteger)column
									  usableWidth:(CGFloat)usableWidth
{
	//XT_DEF_SELNAME;
	
	CGFloat totalMaxColumnWidthIncludingBounds = [self totalMaxColumnWidthIncludingBounds];
	CGFloat totalMaxColumnWidthExcludingBounds = [self totalMaxColumnWidthExcludingBounds];
	CGFloat widthToDistribute = usableWidth - totalMaxColumnWidthIncludingBounds;
	XTTableColumnInfo *columnInfo = [self getColumnInfo:column];
	CGFloat fractionExtraForThisColumn = columnInfo.maxContentRectWidth.floatValue / totalMaxColumnWidthExcludingBounds;
	CGFloat extraForThisColumn = widthToDistribute * fractionExtraForThisColumn;
	contectRectWidth += extraForThisColumn;
	
	//XT_WARN_3(@"row col %ld %ld -> %lf", textTableBlock.startingRow, textTableBlock.startingColumn, contectRectWidth);
	return contectRectWidth;
}

- (CGFloat)totalMinColumnWidthExcludingBounds
{
	CGFloat res = 0.0;
	for (XTTableColumnInfo *columnInfo in self.columnInfoByColumn.allValues) {
		res += columnInfo.minContentRectWidth.doubleValue;
	}
	return res;
}

- (CGFloat)totalMaxColumnWidthExcludingBounds
{
	CGFloat res = 0.0;
	for (XTTableColumnInfo *columnInfo in self.columnInfoByColumn.allValues) {
		res += columnInfo.maxContentRectWidth.doubleValue;
	}
	return res;
}

- (CGFloat)totalMinColumnWidthIncludingBounds
{
	//XT_DEF_SELNAME;

	CGFloat res = 0.0;
	for (XTTableColumnInfo *columnInfo in self.columnInfoByColumn.allValues) {
		res += columnInfo.minContentRectWidth.doubleValue;
		res += columnInfo.totalBoundsWidth.doubleValue;
		//XT_WARN_2(@"res += : minContentRectWidth=%lf totalBoundsWidth=%lf", columnInfo.minContentRectWidth.doubleValue, columnInfo.totalBoundsWidth.doubleValue);
	}
	return res;
}

- (CGFloat)totalMaxColumnWidthIncludingBounds
{
	CGFloat res = 0.0;
	for (XTTableColumnInfo *columnInfo in self.columnInfoByColumn.allValues) {
		res += columnInfo.maxContentRectWidth.doubleValue;
		res += columnInfo.totalBoundsWidth.doubleValue;
	}
	return res;
}

- (CGFloat)totalWidthOfBounds
{
	CGFloat res = 0.0;
	for (XTTableColumnInfo *columnInfo in self.columnInfoByColumn.allValues) {
		res += columnInfo.totalBoundsWidth.doubleValue;
	}
	return res;
}

- (CGFloat)getForcefullyTruncatedContentRectWidthForColumn:(NSInteger)column
											   usableWidth:(CGFloat)usableWidth
{
	//XT_DEF_SELNAME;

	NSNumber *columnObj = [NSNumber numberWithInteger:column];
	XTTableColumnInfo *columnInfo = self.columnInfoByColumn[columnObj];
	CGFloat minColumnWidth = columnInfo.minContentRectWidth.doubleValue;

	CGFloat totalMinColumnWidthExcludingBounds = [self totalMinColumnWidthExcludingBounds];
	
	CGFloat pct = (minColumnWidth / totalMinColumnWidthExcludingBounds) * 100.0;
	if (pct > 100.0) {
		int brkpt = 1;
	}

	CGFloat totalWidthOfBounds = [self totalWidthOfBounds];
	CGFloat usableWidthToDistribute = usableWidth - totalWidthOfBounds;
	
	CGFloat res = (usableWidthToDistribute * pct) / 100.0;
	if (res < 1.0) {
		int brkpt = 1;
	}
	
	//XT_WARN_6(@"row=%d col=%d usableWidth=%lf totalMinColumnWidthExcludingBounds=%lf totalWidthOfBounds=%lf --> %lf",
	//		  textTableBlock.startingRow, textTableBlock.startingColumn, usableWidth, totalMinColumnWidthExcludingBounds, totalWidthOfBounds, res);

	return res;
}

- (CGFloat)getContentRectWidthForColumn:(NSInteger)column
							usableWidth:(CGFloat)usableWidth
{
	//XT_DEF_SELNAME;

	NSNumber *columnObj = [NSNumber numberWithInteger:column];
	XTTableColumnInfo *columnInfo = self.columnInfoByColumn[columnObj];
	NSUInteger columnCount = self.columnInfoByColumn.count;
	
	NSMutableDictionary<NSNumber*, NSNumber*> *contentRectWidthByColumn = [NSMutableDictionary dictionaryWithCapacity:columnCount];
	
	// Find remaining usable width:
	//-----------------------------

	CGFloat usableWidthLeft = usableWidth;
	//XT_WARN_3(@"---- column=%ld columnCount=%ld usableWidth=%ld", column, columnCount, (NSInteger)usableWidthLeft);
	
	for (NSNumber *columnObj in self.columnInfoByColumn.allKeys) {
		
		XTTableColumnInfo *columnInfo = self.columnInfoByColumn[columnObj];
		usableWidthLeft -= columnInfo.totalBoundsWidth.doubleValue;
		usableWidthLeft -= columnInfo.minContentRectWidth.doubleValue;
		//XT_WARN_4(@"usableWidthLeft=%ld -> %ld (%ld) for column %ld", (NSInteger)oldUsableWidthLeft, (NSInteger)usableWidthLeft, (NSInteger)change, columnObj.integerValue);
		if (usableWidthLeft < 0.0) {
			XT_DEF_SELNAME;
			XT_WARN_2(@"usableWidthLeft=%lf for column %ld", usableWidthLeft, columnObj.integerValue);
		}
	}

	// Find content widths of abs-width columns:
	//------------------------------------------

	//XT_WARN_0(@"abs-width columns...");
	for (NSNumber *columnObj in self.columnInfoByColumn.allKeys) {
		XTTableColumnInfo *columnInfo = self.columnInfoByColumn[columnObj];
		CGFloat contentRectWidth = columnInfo.minContentRectWidth.doubleValue;
		if (columnInfo.minContentRectWidth.doubleValue == columnInfo.maxContentRectWidth.doubleValue) {
			contentRectWidthByColumn[columnObj] = [NSNumber numberWithDouble:contentRectWidth];
			//XT_WARN_2(@"abs-width column %ld width = %lf", columnObj.integerValue, width);
		}
	}
	
	// Distribute remaining usable width among non-abs-width columns:
	//---------------------------------------------------------------

	// https://drafts.csswg.org/css3-tables-algorithms/Overview.src.htm
	
	CGFloat usableWidthToDistribute = usableWidthLeft;
	//XT_WARN_1(@"usableWidthToDistribute=%ld", (NSInteger)usableWidthToDistribute);
	
	CGFloat totalDiffBetweenMaxAndMinContentRectWidths = 0.0;
	for (NSNumber *columnObj in self.columnInfoByColumn.allKeys) {
		if (contentRectWidthByColumn[columnObj] == nil) {
			XTTableColumnInfo *columnInfo = self.columnInfoByColumn[columnObj];
			CGFloat maxContentRectWidth = columnInfo.maxContentRectWidth.doubleValue;
			CGFloat minContentRectWidth = columnInfo.minContentRectWidth.doubleValue;
			totalDiffBetweenMaxAndMinContentRectWidths += maxContentRectWidth;
			totalDiffBetweenMaxAndMinContentRectWidths -= minContentRectWidth;
		}
	}
	//XT_WARN_1(@"totalMaxWidthOfNonAbsWidthColumns=%ld", (NSInteger)totalMaxWidthOfNonAbsWidthColumns);
	
	//XT_WARN_0(@"non-abs-width columns...");
	for (NSNumber *columnObj in self.columnInfoByColumn.allKeys) {

		XTTableColumnInfo *columnInfo = self.columnInfoByColumn[columnObj];
		if (contentRectWidthByColumn[columnObj] == nil) {
			// It's a column whose content width must be calc'd as: min content width + pct of usableWidthToDistribute
			//XT_WARN_1(@"non-abs-width columns %ld", columnObj.integerValue);
			CGFloat maxContentRectWidth = columnInfo.maxContentRectWidth.doubleValue;
			CGFloat minContentRectWidth = columnInfo.minContentRectWidth.doubleValue;
			CGFloat pct = ((maxContentRectWidth - minContentRectWidth) / totalDiffBetweenMaxAndMinContentRectWidths) * 100.0;
			if (pct > 100.0) {
				int brkpt = 1;
			}
			CGFloat portionOfUsableWidthToDistribute = (usableWidthToDistribute * pct) / 100.0;
			if (portionOfUsableWidthToDistribute < 0.0) {
				portionOfUsableWidthToDistribute = 0.0;
			}
			usableWidthLeft -= portionOfUsableWidthToDistribute;
			CGFloat contentRectWidth = minContentRectWidth + portionOfUsableWidthToDistribute;
			contentRectWidth = contentRectWidth;
			contentRectWidthByColumn[columnObj] = [NSNumber numberWithDouble:contentRectWidth];
			/*
			XT_WARN_5(@"non-abs-width column %ld: maxColumnWidth=%ld minColumnWidth=%ld pct=%ld width=%ld",
					  columnObj.integerValue, (NSInteger)maxColumnWidth, (NSInteger)minColumnWidth, (NSInteger)pct, (NSInteger)width);
			 */
		}
	}
	
	// Distribute any remaining (from rounding down) usableWidthLeft:
	//---------------------------------------------------------------

	// (Never seems necessary...)
	
	// Find and sanitize the actual column width:
	//-------------------------------------------

	NSNumber *widthObj = contentRectWidthByColumn[columnObj];
	if (widthObj == nil) {
		XT_DEF_SELNAME;
		XT_ERROR_0(@"widthObj == nil");
	}
	CGFloat width = widthObj.doubleValue;
	if (width < columnInfo.minContentRectWidth.doubleValue) {
		XT_DEF_SELNAME;
		XT_WARN_2(@"width (%lf) < columnInfo.minContentRectWidth.doubleValue (%lf)", width, columnInfo.minContentRectWidth.doubleValue);
		width = columnInfo.minContentRectWidth.doubleValue;
	}
	
	return width;
}

- (CGFloat)contentRectXForTextTableBlock:(XTTextTableBlock *)textTableBlock
								 originX:(CGFloat)originX
							 usableWidth:(CGFloat)usableWidth
{
	//XT_DEF_SELNAME;
	
	NSInteger column = textTableBlock.startingColumn;
	CGFloat res = originX;
	//CGFloat res0 = originX;

	CGFloat totalTableWidth = 0.0;
	
	for (NSNumber *columnObj in self.columnInfoByColumn.allKeys) {
		XTTableColumnInfo *columnInfo = self.columnInfoByColumn[columnObj];
		CGFloat colWidth = columnInfo.contentRectWidth.doubleValue + columnInfo.totalBoundsWidth.doubleValue;
		if (columnObj.integerValue < column) {
			// We're to the left of textTableBlock's column
			
			//res0 += columnInfo.maxBoundsRectWidth.doubleValue;
			//res += columnInfo.contentRectWidth.doubleValue;
			//res += columnInfo.totalBoundsWidth.doubleValue;
			res += colWidth;
			//if (res0 != res) {
			//	int brkpt = 1;
			//}
			
			//TODO !!! might mess with td margin=
			//TODO !!! use cell level border size too?
		//} else if (columnObj.integerValue <= column) {
			//totalTableWidth += columnInfo.maxBoundsRectWidth.doubleValue;
				//TODO !!! align with totalTableWidth func?
		}
		totalTableWidth += colWidth;
	}

	res += [textTableBlock totalBoundsWidthOnLeftHandSide];
	//XT_WARN_1(@"--> %lf", res);
	
	XTTextTable *table = (XTTextTable *)textTableBlock.table;
	if (table.alignment == XT_TEXT_ALIGN_RIGHT || table.alignment == XT_TEXT_ALIGN_CENTER) {
		//CGFloat totalTableWidth = [self totalTableWidth];
		// Note: On window resize, totalTableWidth is inaccurate because it's based on the previous window size.
		//       To compensate for this, we re-layout the window after each resize step
			//TODO !!! do re-layout even after first-time layout?
		CGFloat widthToDistribute = usableWidth - totalTableWidth;
		if (widthToDistribute >= 2.0) {
			// Note: test against 2.0 because we don't always use the remainder. Prevents jittery lhs x pos.
			CGFloat adjustment = widthToDistribute;
			if (table.alignment == XT_TEXT_ALIGN_CENTER) {
				adjustment = adjustment / 2.0;
			}
			res += adjustment;
			//XT_WARN_5(@"row=%ld col=%ld usableWidth=%lf totalTableWidth=%lf -> adjustment=%lf",
			//		  textTableBlock.startingRow, textTableBlock.startingColumn, usableWidth, totalTableWidth, adjustment);
		} else {
			//XT_WARN_0(@"other");
			//XT_WARN_5(@"row=%ld col=%ld usableWidth=%lf totalTableWidth=%lf -> adjustment=%lf",
			//		  textTableBlock.startingRow, textTableBlock.startingColumn, usableWidth, totalTableWidth, 0.0);
		}
	}

	//TODO !!! test makes no sense:
	//if (res + usableWidth > usableWidth) {
	//	int brkpt = 1;
	//}

	return res;
}

- (CGFloat)totalTableWidth
{
	CGFloat res = 0.0;

	for (NSNumber *columnObj in self.columnInfoByColumn.allKeys) {
		XTTableColumnInfo *columnInfo = self.columnInfoByColumn[columnObj];
		res += columnInfo.boundsRectWidth.doubleValue;
	}

	return res;
}

- (CGFloat)visualWidthInclColumnMarginOfTableUpToColumn:(NSInteger)column
{
	CGFloat res = 0.0;
	
	for (NSNumber *columnObj in self.columnInfoByColumn.allKeys) {
		if (columnObj.integerValue < column) {
			XTTableColumnInfo *columnInfo = self.columnInfoByColumn[columnObj];
			res += columnInfo.boundsRectWidth.doubleValue;
		}
	}
	
	NSNumber *columnObj = [NSNumber numberWithInteger:column];
	if (columnObj != nil) {
		XTTableColumnInfo *columnInfo = self.columnInfoByColumn[columnObj];
		CGFloat toAdd = (columnInfo.totalBoundsWidth.doubleValue / 2.0);
		toAdd = ceil(toAdd);
		res += toAdd;
	}
	
	return res;
}

- (void)clear
{
	[self.columnInfoByColumn removeAllObjects];
}

- (XTTableColumnInfo *)getOrCreateColumnInfo:(NSInteger)column
{
	XTTableColumnInfo *columnInfo = [self getColumnInfo:column];
	if (columnInfo == nil) {
		columnInfo = [XTTableColumnInfo new];
		NSNumber *columnObj = [NSNumber numberWithInteger:column];
		self.columnInfoByColumn[columnObj] = columnInfo;
	}
	return columnInfo;
}

- (XTTableColumnInfo *)getColumnInfo:(NSInteger)column
{
	NSNumber *columnObj = [NSNumber numberWithInteger:column];
	XTTableColumnInfo *columnInfo = self.columnInfoByColumn[columnObj];
	return columnInfo;
}

@end
