//
//  NumericalArrayDS.h
//
//  Numerical array data sources for NSTableView.  Simplifies the binding of
//  a simple array of numbers (integers and floating point types) to a
//  Cocoa table.
//
//  This source code is provided as-is, and I provide no warranty as to its
//  functioning.  That being said, I have done a bit of testing with it and
//  it seems to work fine.
//
//  No explicit licensing is attached to this source code, I merely ask
//  that you let me know if it was useful to you in your project, and if you
//  would just make mention in the documentation somewhere that you used
//  this work.
//
//  Created by Jeffrey Frey on 28 Jan 2007
//  Copyright (c) 2007
//

#import <Cocoa/Cocoa.h>

/*!
  @protocol NumericalArrayAccess
  
  Methods that must be implemented by any numerical array-representing class
  that wishes to use the NumericalArrayDS for tabular display.
*/
@protocol NumericalArrayAccess

- (int) rows;
- (int) columns;
- (NSNumberFormatter*) defaultFormatter;
- (NSNumber*) valueAtRow:(int)row column:(int)column;
- (void) setValue:(id)value atRow:(int)row column:(int)column;

@end

//
// Some pre-defined numerical array represetation classes.
//

/*!
  @typedef NumericalArrayType
  
  The C type of numerical data in the array.
*/
typedef enum {
  NumericalArrayTypeInt       = 0,
  NumericalArrayTypeLong,
  NumericalArrayTypeLongLong,
  NumericalArrayTypeFloat,
  NumericalArrayTypeDouble,
  
  NumericalArrayTypeMax
} NumericalArrayType;

/*!
  @typedef NumericalArrayOrientation
  
  The orientation of the stored data.  Column-major implies
  that the first N bytes represent the first column of an
  N x M array; row-major implies that the first M bytes
  represent the first row of an N x M array.
*/
typedef enum {
  NumericalArrayOrientationColumnMajor,
  NumericalArrayOrientationRowMajor
} NumericalArrayOrientation;

/*!
  @class NumericalArray
  
  Generic wrapper for C numerical arrays.  Handles several integer types, floats, and
  doubles, in both row- and column-major storage formats.
  
  Given a 25 x 25 array of float's stored row-major, an appropriate allocation would
  be:
  
  <pre>
float             dummy[25 * 25];
NumericalArray*   myArrayRep;

myArrayRep = [[NumericalArray alloc] initWithArrayType:NumericalArrayTypeFloat
               array:dummy
               rows:25
               columns:25
               orientation:NumericalArrayOrientationRowMajor];
  </pre>
*/
@interface NumericalArray : NSObject<NumericalArrayAccess>
{
  NumericalArrayType          _arrayType;
  NumericalArrayOrientation   _orientation;
  
  int                         _rows,_columns;
  void*                       _array;
  BOOL                        _freeWhenDone;
}

/*!
  @method initWithArrayType:array:rows:columns:
  
  Convenience method that calls initWithArrayType:array:rows:columns:orientation: with
  an orientation of NumericalArrayOrientationColumnMajor.
*/
- (id) initWithArrayType:(NumericalArrayType)type array:(void*)array rows:(int)rows columns:(int)columns;
/*!
  @method initWithArrayType:array:rows:columns:orientation:
  
  Convenience method that calls initWithArrayType:array:rows:columns:orientation:fwd: with
  fwd set to NO.
*/
- (id) initWithArrayType:(NumericalArrayType)type array:(void*)array rows:(int)rows columns:(int)columns orientation:(NumericalArrayOrientation)orientation;
/*!
  @method initWithArrayType:array:rows:columns:orientation:freeWhenDone:
  
  Designated initializer.  The array enters the receiver as a typeless pointer
  since we internally deal with several different types of atomic numerical
  data.  If array is NULL, then the storage is allocated by this method and will
  be deallocated when the instance is.  If you pass a non-NULL array, then the
  value of fwd determines whether or not the array should be free'd when the
  instance is deallocated.
*/
- (id) initWithArrayType:(NumericalArrayType)type array:(void*)array rows:(int)rows columns:(int)columns orientation:(NumericalArrayOrientation)orientation freeWhenDone:(BOOL)fwd;

/*!
  @method arrayType
  
  Returns the NumericalArrayType of the receiver's array.
*/
- (NumericalArrayType) arrayType;
/*!
  @method orientation
  
  Returns the NumericalArrayOrientation of the receiver's array.
*/
- (NumericalArrayOrientation) orientation;
/*!
  @method array
  
  Returns the receiver's array (as a typeless pointer).
*/
- (void*) array;

@end

/*!
  @typedef NumericalArrayTriangular
  
  Enumerated constants used to indicate that a numerical array
  is upper- or lower-triangular.
*/
typedef enum {
  NumericalArrayTriangularUpper,
  NumericalArrayTriangularLower
} NumericalArrayTriangular;

/*!
  @class NumericalTrianularArray
  
  Generic wrapper for C triangular numerical arrays, implemented as a
  simple sub-class of the NumericalArray.
*/
@interface NumericalTrianularArray : NumericalArray
{
  NumericalArrayTriangular    _triangularity;
}

/*!
  @method initWithUpperTriangularArrayType:array:rows:columns:
  
  Convenience method that calls initWithUpperTriangularArrayType:array:rows:columns:orientation: with
  an orientation of NumericalArrayOrientationColumnMajor.
*/
- (id) initWithUpperTriangularArrayType:(NumericalArrayType)type array:(void*)array rows:(int)rows columns:(int)columns;
/*!
  @method initWithUpperTriangularArrayType:array:rows:columns:orientation:
  
  Designated initializer.  The array enters the receiver as a typeless pointer
  since we internally deal with several different types of atomic numerical
  data.
*/
- (id) initWithUpperTriangularArrayType:(NumericalArrayType)type array:(void*)array rows:(int)rows columns:(int)columns orientation:(NumericalArrayOrientation)orientation;
/*!
  @method initWithLowerTriangularArrayType:array:rows:columns:
  
  Convenience method that calls initWithLowerTriangularArrayType:array:rows:columns:orientation: with
  an orientation of NumericalArrayOrientationColumnMajor.
*/
- (id) initWithLowerTriangularArrayType:(NumericalArrayType)type array:(void*)array rows:(int)rows columns:(int)columns;
/*!
  @method initWithLowerTriangularArrayType:array:rows:columns:orientation:
  
  Designated initializer.  The array enters the receiver as a typeless pointer
  since we internally deal with several different types of atomic numerical
  data.
*/
- (id) initWithLowerTriangularArrayType:(NumericalArrayType)type array:(void*)array rows:(int)rows columns:(int)columns orientation:(NumericalArrayOrientation)orientation;

/*!
  @method triangularity
  
  Triangularity of the receiver's array.
*/
- (NumericalArrayTriangular) triangularity;

@end

//
// Now for the NSTableView facilitator itself:
//

/*!
  @class NumericalArrayDS
  
  Facilitates easy reconfiguration of an NSTableView to display a two-dimensional
  array of numerical values.  Instances are immutable and act as the NSTableView
  data source (hence the 'DS' in the class name) and move data array => NSTableView
  as well as NSTableView => array -- so you get a simple i/o interface between the
  UI and your array.
  
  The data source must then be bound to an NSTableView in order for the table to
  be reconfigured to the appropriate number of columns and formatting.  You may
  supply a dictionary of display parameters or allow the binding method to setup
  default values for you.
  
  <pre>
NSDictionary*     attrs;

attrs = [NSDictionary dictionaryWithObjectsAndKeys:
            [NSNumber numberWithInt:100],NumericalArrayDSMaximumColumnWidthKey,
            [NSFont userFixedPitchFontOfSize:12.0f],NumericalArrayDSFontKey,
            nil];
[myDS bindToTableView:aTableView displayAttributes:attrs];
  </pre>
  
  In this case I specified that the columns of the table should be no larger than
  100 pixels and the font should be fixed-pitch, 12.0 points large.
*/
@interface NumericalArrayDS : NSObject
{
  id        _array;
}

/*!
  @method initWithNumericalArray:
  
  Designated initializer.  If array is an object whose class does not
  conform to the NumericalArrayAccess protocol, nil is returned.  Otherwise,
  array is sent a retain message and the newly initialized instance is
  returned.
*/
- (id) initWithNumericalArray:(id)array;

/*!
  @method bindToTableView:displayAttributes:
  
  Removes all columns currently in aTable and replaces them with a uniform
  set of columns configured to display the numerical array.  A set of display
  options may be passed to this method in an NSDictionary:
  
  - An NSCell that should act as the template for all columns
  - The initial, minimum, and maximum widths of the columns
  
  Lacking a template NSCell in the dictionary this method will create a default
  template NSCell.  You may override the following default attributes with values
  in the displayAttributes dictionary:
  
  - The font
  - The NSNumberFormatter used to display the values
  
  Once the template NSCell has been constructed and the new columns created for
  the table, the receiver is set as both the delegate and data source of aTable
  and aTable is told to reload (and thus redisplay its content).
*/
- (void) bindToTableView:(NSTableView*)aTable withDisplayAttributes:(NSDictionary*)displayAttributes;

#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
/*!
  @method tableView:toolTipForCell:rect:tableColumn:row:mouseLocation:
  
  NSTableView delegate method.
  
  Displays a tooltip over a table cell -- we merely show the row and column of the
  cell.
*/
- (NSString*) tableView:(NSTableView*)tv toolTipForCell:(NSCell*)cell rect:(NSRectPointer)rect tableColumn:(NSTableColumn*)tc row:(int)row mouseLocation:(NSPoint)mouseLocation;
#endif

/*!
  @method numberOfRowsInTableView:
  
  NSTableDataSource informal protocol method.
  
  Returns the number of rows in the table.
*/
- (int) numberOfRowsInTableView:(NSTableView*)aTableView;
/*!
  @method tableView:objectValueForTableColumn:row:
  
  NSTableDataSource informal protocol method.
  
  Returns the value (as an NSNumber) of the specified row and column of the
  table.
*/
- (id) tableView:(NSTableView*)aTableView objectValueForTableColumn:(NSTableColumn*)aTableColumn row:(int)rowIndex;
/*!
  @method tableView:setObjectValue:forTableColumn:row:
  
  NSTableDataSource informal protocol method.
  
  Sets the value (as an NSNumber or NSString) of the specified row and column of the
  table.
*/
- (void) tableView:(NSTableView*)aTableView setObjectValue:(id)anObject forTableColumn:(NSTableColumn*)aTableColumn row:(int)rowIndex;

@end

/*!
  @const NumericalArrayDSCellTemplateKey
  Keys a pre-constructed NSCell template that should be used by bindToTableView:displayAttributes:
  when it constructs the NSTableColumn instances.
*/
APPKIT_EXTERN NSString* NumericalArrayDSCellTemplateKey;
/*!
  @const NumericalArrayDSFontKey
  Lacking an NSCell template, keys the font that is to be used in the cell template that
  bindToTableView:displayAttributes: itself constructs.
*/
APPKIT_EXTERN NSString* NumericalArrayDSFontKey;
/*!
  @const NumericalArrayDSFormatterKey
  Lacking an NSCell template, keys the NSNumberFormatter that is to be used in the cell
  template that bindToTableView:displayAttributes: itself constructs.
*/
APPKIT_EXTERN NSString* NumericalArrayDSFormatterKey;
/*!
  @const NumericalArrayDSInitialColumnWidthKey
  Keys an NSNumber specifying the initial width of the new columns that
  bindToTableView:displayAttributes: constructs.  Defaults to 25 pixels.
*/
APPKIT_EXTERN NSString* NumericalArrayDSInitialColumnWidthKey;
/*!
  @const NumericalArrayDSMinimumColumnWidthKey
  Keys an NSNumber specifying the minimum width of the new columns that
  bindToTableView:displayAttributes: constructs.  Defaults to 50 pixels.
*/
APPKIT_EXTERN NSString* NumericalArrayDSMinimumColumnWidthKey;
/*!
  @const NumericalArrayDSMaximumColumnWidthKey
  Keys an NSNumber specifying the maximum width of the new columns that
  bindToTableView:displayAttributes: constructs.  The maximum is unlimited
  by default.
*/
APPKIT_EXTERN NSString* NumericalArrayDSMaximumColumnWidthKey;
