#import <appkit/Application.h>
#import <appkit/Font.h>

#import "misckit/MiscCalendarViewConstants.h"
#import "misckit/DateDelegateProtocol.h"
#import "MiscCalendarMatrix.h"
#import "DateSelectionCell.h"
#import "SimpleDate.h"

@implementation MiscCalendarMatrix

+ initialize
{
    [MiscCalendarMatrix setVersion:1];

    return self;
}

- initFrame:(const NXRect *)frameRect
{
    NXSize 	spacing,
		newCellSize;
    id		aCell;

    [super initFrame:frameRect mode:NX_RADIOMODE 
     cellClass:[DateSelectionCell class] numRows:NUM_MATRIX_ROWS
     numCols:NUM_MATRIX_COLS];

    [self setEmptySelectionEnabled:YES];

    [self clearSelectedCell];

    [Font setUserFont:[Font newFont:"Helvetica" size:11 style:0
		       matrix:NX_FLIPPEDMATRIX]];

    [self setFont:[Font newFont:"Helvetica" size:11 style:0
		   matrix:NX_FLIPPEDMATRIX]];

    spacing.height = MATRIX_SPACING_HEIGHT;
    spacing.width = MATRIX_SPACING_WIDTH;

    [self setIntercell:&spacing];

    newCellSize.height = MATRIX_CELL_HEIGHT;
    newCellSize.width = MATRIX_CELL_WIDTH;

    [self setCellSize:&newCellSize];

    [self sizeToCells];

    [self sizeBy:1 :1];

    simpleDate = [[SimpleDate alloc] init];

    [self setDateDelegate:simpleDate];

    aCell = [[self cellList] objectAt:0];

    cellTextColor = [aCell textColor];
    cellHighlightTextColor = [aCell highlightTextColor];
    cellHighlightColor = [aCell highlightColor];
    cellBackgroundColor = [aCell backgroundColor];

    overwriteCellColors = NO;

    [self displayDate:self];

    return self;
}

- initFrame:(const NXRect *)frameRect mode:(int)aMode cellClass:cellId
  numRows:(int)numRows numCols:(int)numCols
{
    return [self initFrame:frameRect];
}

- initFrame:(const NXRect *)frameRect mode:(int)aMode prototype:aCell
  numRows:(int)numRows numCols:(int)numCols
{
    return [self initFrame:frameRect];
}

- free
{
    [simpleDate free];

    return [super free];
}

- setDateDelegate:aDateObject
{
    if ([aDateObject conformsTo:@protocol(DateDelegate)])
      dateDelegate = aDateObject;

    return self;
}

- dateDelegate
{
    return dateDelegate;
}

- overwriteCellColors:(BOOL)yn
{
    overwriteCellColors = yn;

    return self;
}

- (BOOL)cellColorsOverwritten
{
    return overwriteCellColors;
}

- setCellBackgroundColor:(NXColor)aColor
{
    List	*myCellList;
    id		aCell;
    int		i;

    myCellList = [self cellList];

    for (i = 0; i < [myCellList count]; i++)
    {
	aCell = [myCellList objectAt:i];

	if (overwriteCellColors || 
	    NXEqualColor(cellBackgroundColor, [aCell backgroundColor]))
	  [aCell setBackgroundColor:aColor];
    }

    cellBackgroundColor = aColor;

    return self;
}

- (NXColor)cellBackgroundColor
{
    return cellBackgroundColor;
}

- setBackgroundColor:(NXColor)aColor forCellAt:(int)xPos :(int)yPos
{
    [[self cellAt:xPos :yPos] setBackgroundColor:aColor];

    return self;
}

- (NXColor)backgroundColorForCellAt:(int)xPos :(int)yPos
{
    return [[self cellAt:xPos :yPos] backgroundColor];
}

- setCellHighlightColor:(NXColor)aColor
{
    List	*myCellList;
    id		aCell;
    int		i;

    myCellList = [self cellList];

    for (i = 0; i < [myCellList count]; i++)
    {
	aCell = [myCellList objectAt:i];

	if (overwriteCellColors || 
	    NXEqualColor(cellHighlightColor, [aCell highlightColor]))
	  [aCell setHighlightColor:aColor];
    }

    cellHighlightColor = aColor;

    return self;
}

- (NXColor)cellHighlightColor
{
    return cellHighlightColor;
}

- setHighlightColor:(NXColor)aColor forCellAt:(int)xPos :(int)yPos
{
    [[self cellAt:xPos :yPos] setHighlightColor:aColor];

    return self;
}

- (NXColor)highlightColorForCellAt:(int)xPos :(int)yPos
{
    return [[self cellAt:xPos :yPos] highlightColor];
}

- setCellTextColor:(NXColor)aColor
{
    List	*myCellList;
    id		aCell;
    int		i;

    myCellList = [self cellList];

    for (i = 0; i < [myCellList count]; i++)
    {
	aCell = [myCellList objectAt:i];
	if (overwriteCellColors || 
	    NXEqualColor(cellTextColor, [aCell textColor]))
	[aCell setTextColor:aColor];
    }

    cellTextColor = aColor;

    return self;
}

- (NXColor)cellTextColor
{
    return cellTextColor;
}

- setTextColor:(NXColor)aColor forCellAt:(int)xPos :(int)yPos
{
    [[self cellAt:xPos :yPos] setTextColor:aColor];

    return self;
}

- (NXColor)textColorForCellAt:(int)xPos :(int)yPos
{
    return [[self cellAt:xPos :yPos] textColor];
}

- setCellHighlightTextColor:(NXColor)aColor
{
    List	*myCellList;
    id		aCell;
    int		i;

    myCellList = [self cellList];

    for (i = 0; i < [myCellList count]; i++)
    {
	aCell = [myCellList objectAt:i];

	if (overwriteCellColors || 
	    NXEqualColor(cellHighlightTextColor, [aCell highlightTextColor]))
	  [aCell setHighlightTextColor:aColor];
    }

    cellHighlightTextColor = aColor;

    return self;
}

- (NXColor)cellHighlightTextColor
{
    return cellHighlightTextColor;
}

- setHighlightTextColor:(NXColor)aColor forCellAt:(int)xPos :(int)yPos
{
    [[self cellAt:xPos :yPos] setHighlightTextColor:aColor];

    return self;
}

- (NXColor)highlightTextColorForCellAt:(int)xPos :(int)yPos
{
    return [[self cellAt:xPos :yPos] highlightTextColor];
}

- mouseDown:(NXEvent *)theEvent
{
    id		theCell = nil,
		previousCell = nil;
    NXEvent	*nextEvent;
    NXPoint	mouseLocation;
    BOOL	looping = YES;
    int		row = -1,
		col = -1,
    		previousRow = -1,
    		previousCol = -1,
		oldMask;

    nextEvent = theEvent;

    if ([self mode] == NX_HIGHLIGHTMODE)
    {
	/* Save our normal window event mask, and add the mouse dragged */
	/* event mask */

	oldMask = [window addToEventMask:NX_MOUSEDRAGGEDMASK];

	while (looping)
	{
	    /* Find the current coordinates of the mouse... */

	    mouseLocation = nextEvent->location;

	    /* ... and convert them into our view coordinate system */

	    [self convertPoint:&mouseLocation fromView:nil];

	    /* Find the row and column of the mouse */

	    [self getRow:&row andCol:&col forPoint:&mouseLocation];

	    if ( (row >= 0) && (col >= 0) && ((row != previousRow) ||
					      (col != previousCol)) )
	    {
		theCell = [self cellAt:row :col];

		if ([theCell isEnabled])
		{
		    if (previousCell && (previousCell != theCell))
		      [previousCell incrementState];

		    [theCell incrementState];

		    [self display];

		    previousCell = theCell;
		}

		previousRow = row;
		previousCol = col;
	    }
	    else if (((row < 0) || (col < 0)) && previousCell && 
		     [previousCell isEnabled])
	    {
		[previousCell incrementState];
		[self display];
		previousCell = nil;
	    }
		
	    nextEvent = [NXApp getNextEvent:NX_MOUSEUPMASK | 
			 NX_MOUSEDRAGGEDMASK];

	    looping = (nextEvent->type == NX_MOUSEDRAGGED);
	}

	[window setEventMask:oldMask];
    }
    else
      return [super mouseDown:theEvent];

    return self;
}

- displayDate:sender
{
    int	numberOfDays,
	startCell,
    	currentDay = 0,
	x,
	y,
    	day;
    id	dayCell,
	aDate = nil;
    
    [[self window] disableFlushWindow];


    if ((aDate = [self dateDelegate]))
    {
	currentDay = [aDate day];

	/* Get the number of days for the month that the date object */
	/* is set to */

	numberOfDays = [aDate numberOfDaysInMonth];

	/* Get the starting day of the month */

	startCell = [aDate startDayOfMonth];

    }
    else
    {
	currentDay = 1;
	numberOfDays = 31;
	startCell = 4;
    }

    // Clear out the titles of the first row of dayCells
    // from the start of the matrix to the x coordinate
    // that corresponds to the day of the week that the
    // month start on
    
    for (x=0, y=0; x < startCell; x++)
    {
	dayCell = [self cellAt:y :x];

	if (x == 0)
	  [dayCell setDrawEntireBottomEdge:NO];

	[dayCell setStringValue:""];
	[dayCell setEnabled:NO];
    }
    
    // Fill in the titles of all the dayCells. When x hits
    // the seventh column of the matrix, we wrap around
    // to the first column and continue filling in values
    // on the next row
    
    for (day = 1; day <= numberOfDays; day++, x++)
    {
	if (x == 7)
	{
	    x = 0;
	    y++;
	}

	dayCell = [self cellAt:y :x];

	[dayCell setIntValue:day];
	[dayCell setEnabled:YES];

	if (day == currentDay)
	  [self selectCellAt:y :x];

	if (x == 0)
	  [dayCell setDrawEntireBottomEdge:NO];

	if (x == 6)
	  [dayCell setDrawRightEdge:YES];
    }
    
    // Clear out the titles of the rest of the dayCells
    // from the last day of the month to the end of
    // the matrix. When x hits the seventh column of 
    // the matrix, we wrap around to the first column
    // and continue blanking dayCells on the next row until
    // y hits the seventh row. We are then done with 
    // this matrix
    
    for (; y < 7; x++)
    {
	if (x == 7)
	{
	    x = 0;
	    y++;
	}
	
	dayCell = [self cellAt:y :x];

	if (x == 0)
	  [dayCell setDrawEntireBottomEdge:NO];

	if (x == 6)
	  [dayCell setDrawRightEdge:YES];

	[dayCell setStringValue:""];
	[dayCell setEnabled:NO];

    }
    
    [self display];

    [[self window] reenableFlushWindow];
    [[self window] flushWindowIfNeeded];

    return self;
}

- read:(NXTypedStream *)aStream
{
    float	red,
		green,
		blue;
    int 	version;

    [super read:aStream];

    version = NXTypedStreamClassVersion(aStream, "MiscCalendarMatrix");

    NXReadTypes(aStream, "@@", &dateDelegate, &simpleDate);

    NXReadTypes(aStream, "fff", &red, &green, &blue);
    cellBackgroundColor = NXConvertRGBToColor(red, green, blue);

    NXReadTypes(aStream, "fff", &red, &green, &blue);
    cellHighlightColor = NXConvertRGBToColor(red, green, blue);

    NXReadTypes(aStream, "fff", &red, &green, &blue);
    cellTextColor = NXConvertRGBToColor(red, green, blue);

    NXReadTypes(aStream, "fff", &red, &green, &blue);
    cellHighlightTextColor = NXConvertRGBToColor(red, green, blue);

    if (version == 1)
      NXReadTypes(aStream, "c", &overwriteCellColors);

    return self;
}

- write:(NXTypedStream *)aStream
{
    float	red,
		green,
		blue;

    [super write:aStream];

    NXWriteTypes(aStream, "@@", &dateDelegate, &simpleDate);

    NXConvertColorToRGBA(cellBackgroundColor, &red, &green, &blue, 0);
    NXWriteTypes(aStream, "fff", &red, &green, &blue);

    NXConvertColorToRGBA(cellHighlightColor, &red, &green, &blue, 0);
    NXWriteTypes(aStream, "fff", &red, &green, &blue);

    NXConvertColorToRGBA(cellTextColor, &red, &green, &blue, 0);
    NXWriteTypes(aStream, "fff", &red, &green, &blue);

    NXConvertColorToRGBA(cellHighlightTextColor, &red, &green, &blue, 0);
    NXWriteTypes(aStream, "fff", &red, &green, &blue);

    NXWriteTypes(aStream, "c", &overwriteCellColors);

    return self;
}

@end
