#ifdef __APPLE_CC__
#include <Carbon/Carbon.h>
#elif TARGET_RT_MAC_MACHO
#include <Carbon/Carbon.h>
#else
#ifndef __MENUS__
#include <Menus.h>
#endif
#endif

#include "ColorMenu.h"
#include "Blugs.h"

#if !TARGET_API_MAC_CARBON
#define SetMenuWidth(menu,width)		((*menu)->menuWidth = width)
#define SetMenuHeight(menu,height)		((*menu)->menuHeight = height)
#define GetMenuWidth(menu)				((*menu)->menuWidth)
#define GetMenuHeight(menu)				((*menu)->menuHeight)
#endif



/*enum
{
	kSquareSize		= 10,
	kSpaceBetween	= 2,
	kSelectionSize	= 1,
	kMenuInset		= 2,
	kPopupInset		= 4
};*/



ColorMenuGlobals	gColorMenuGlobals = {10,2,1,4};


static void SizeMessage( MenuRef menu );
static void DrawMessage( MenuRef menu, Rect menuR );

#if !TARGET_API_MAC_CARBON
static void ChooseMessage( MenuRef menu, Rect menuR, Point hitPt, SInt16* whichItem);
#endif

static SInt16 PointToItem( MenuRef inMenu, Point inWhere, SInt16 inLeft, SInt16 inTop );
static void PopupMessage( MenuRef menu, Rect* r, SInt16 left, SInt16 top );

static void DrawColorSquare( const RGBColor color, SInt16 squareNumber, SInt16 hSquareQty,
							SInt16 left, SInt16 top);
static void GetSquareRect( Rect* squareR, SInt16 squareNumber, SInt16 hSquareQty,
							SInt16 left, SInt16 top );
static SInt16 GetCurrentItem( MenuRef menu, SInt16 qty );
static void		DrawItem( SInt16 inItem, Boolean selected, SInt16 hSquareQty, SInt16 left,
						SInt16 top);
static void local_Dimensions( SInt16 inNItems, SInt16* outNCols, SInt16* outNRows );

#if TARGET_API_MAC_CARBON
static void local_FindItemMessage( MenuRef inMenu, Rect* inMenuRect, Point inWhere,
									MenuTrackingDataPtr ioTrackingData );
static void local_HiliteItemMsg( MenuRef inMenu, Rect* inMenuRect, HiliteMenuItemDataPtr inHiliteData );
#endif

//===================================
// MDEF Code Resource entry point
pascal void ColorMenu( SInt16 message, MenuRef menu, Rect* menuRect, Point hitPt,
						SInt16* whichItem )
{
	switch ( message )
	{
		case kMenuDrawMsg:			// 0
	#if !TARGET_API_MAC_CARBON
		case mDrawItemMsg:			// 4
	#endif
		DrawMessage( menu, *menuRect );
		break;
		
	#if !TARGET_API_MAC_CARBON
		case kMenuChooseMsg:		// 1
		ChooseMessage( menu, *menuRect, hitPt, whichItem );
		break;
	#endif
	
		case kMenuSizeMsg:			// 2
		SizeMessage( menu );
		break;
		
		case kMenuPopUpMsg:			// 3
		// yes, the MacOS reverses the meaning of hitPt.h/v
		PopupMessage( menu, menuRect, hitPt.v, hitPt.h );
		break;
		
		case kMenuThemeSavvyMsg:	// 7
		*whichItem = kThemeSavvyMenuResponse;
		break;
		
	#if TARGET_API_MAC_CARBON
		case kMenuInitMsg:			// 8
		*whichItem = noErr;
		break;
		
		case kMenuFindItemMsg:		// 9
		local_FindItemMessage( menu, menuRect, hitPt, (MenuTrackingDataPtr)whichItem );
		break;
		
		case kMenuHiliteItemMsg:	// 10
		local_HiliteItemMsg( menu, menuRect, (HiliteMenuItemDataPtr)whichItem );
		break;
	#endif
	}
}


//===================================
// determine the size of the menu
// width/height is the square root of the number of items times the size of a sqaure
static void SizeMessage( MenuRef menu )
{
	SInt16		width, height, nItems, nCols, nRows;
	
	nItems = CountMenuItems( menu );
	local_Dimensions( nItems, &nCols, &nRows );
	width = nCols * gColorMenuGlobals.squareSize;
	width += ((nCols + 1) * gColorMenuGlobals.spaceBetween);
	height = nRows * gColorMenuGlobals.squareSize;
	height += ((nRows + 1) * gColorMenuGlobals.spaceBetween);
	SetMenuWidth( menu, width );
	SetMenuHeight( menu, height );
}

//===================================
// draw all of the color chips - this only needs to be done once...
//... the selections don't affect it
static void DrawMessage( MenuRef menu, Rect menuR )
{
	SInt16		qty, i, hSquareQty, left, top;
	RGBColor	greyCol = {56797,56797,56797};
	
	qty = CountMenuItems( menu );
	hSquareQty = sqrt( (float)qty );
	//hSquareQty = SquareRoot( qty );
	left = menuR.left + gColorMenuGlobals.spaceBetween;
	top = menuR.top + gColorMenuGlobals.spaceBetween;
	if (BLAppearanceVersion() > 0x0101)
		DrawThemeMenuBackground( &menuR, kThemeMenuTypePullDown );
	else
	{
		RGBForeColor( &greyCol );
		PaintRect( &menuR );
	}
	for (i = 1; i <= qty; i++)
	{
		Str255		pstr;
		RGBColor	color;
		SInt16		mark;
		
		GetMenuItemText( menu, i, pstr );
		CMPstrToColor( pstr, &color );
		DrawColorSquare( color, i - 1, hSquareQty, left, top);
		GetItemMark( menu, i, &mark);
		if (mark) DrawItem( i - 1, true, hSquareQty, left, top );
	}
}

#if !TARGET_API_MAC_CARBON
//===================================
static void ChooseMessage( MenuRef inMenu, Rect inMenuRect, Point inWhere, SInt16* whichItem)
{
	SInt16		nItems, currentItem, hSquareQty, left, top, newWhichItem;
	
	nItems = CountMenuItems( inMenu );
	currentItem = GetCurrentItem( inMenu, nItems );
	if (!*whichItem) *whichItem = currentItem;
	hSquareQty = SquareRoot( nItems );
	left = inMenuRect.left + gColorMenuGlobals.spaceBetween;
	top = inMenuRect.top + gColorMenuGlobals.spaceBetween;
	newWhichItem = PointToItem( inMenu, inWhere, left, top );
	if (newWhichItem != *whichItem)
	{	
		if (*whichItem) DrawItem( *whichItem - 1, false, hSquareQty, left, top );
		if (newWhichItem) DrawItem( newWhichItem - 1, true, hSquareQty, left, top );
		*whichItem = newWhichItem;
	}
}
#endif

//===============================
// Return 1-based index of the menu item at the specified global coords.
static SInt16 PointToItem( MenuRef inMenu, Point inWhere, SInt16 inLeft, SInt16 inTop )
{
	SInt16	nItems, nRows, nCols, offset, nthCol, nthRow, item = 0;
	
	nItems = CountMenuItems( inMenu );
	local_Dimensions( nItems, &nCols, &nRows );
	offset = inWhere.h - inLeft;
	if (offset >= 0)
	{
		nthCol = (offset / (gColorMenuGlobals.squareSize + gColorMenuGlobals.spaceBetween)) + 1;
		if (nthCol <= nCols)
		{
			offset = inWhere.v - inTop;
			if (offset >= 0)
			{
				nthRow = (offset / (gColorMenuGlobals.squareSize + gColorMenuGlobals.spaceBetween)) + 1;
				if (nthRow <= nRows) item = (nthRow - 1) * nRows + nthCol;
			}
		}
	}
	return item;
}

//===================================
// figure out the popup menu rectangle
static void PopupMessage( MenuRef menu, Rect* r, SInt16 left, SInt16 top )
{
	GDHandle	currDevice;
	Rect		portR;
	SInt16		hDiff = 0, vDiff = 0;
	
	CalcMenuSize( menu );	// this is what CDEF 0 does
	r->left = left;
	r->top = top;
	r->right = r->left + GetMenuWidth( menu );
	r->bottom = r->top + GetMenuHeight( menu );
	currDevice = GetGDevice();
	
	// make sure it's on screen
	portR = (*currDevice)->gdRect;
	portR.top += GetMBarHeight();
	InsetRect( &portR, gColorMenuGlobals.popupInset, gColorMenuGlobals.popupInset);
	
	if (r->left < portR.left) hDiff = portR.left - r->left;
	else if (r->right > portR.right) hDiff = portR.right - r->right;
	if (r->top < portR.top) vDiff = portR.top - r->top;
	else if (r->bottom > portR.bottom) vDiff = portR.bottom - r->bottom;
	// make sure it's not too big
	OffsetRect( r, hDiff, vDiff );
	SectRect( r, &portR, r );
}

//===================================
// return the currently marked item
static SInt16 GetCurrentItem( MenuRef menu, SInt16 qty )
{
	SInt16	i, mark;
	
	// when the Menu Manager wants us to "flash" the new selection
	// it should keep going back and forth to the current item
	// So, recognize this by checking Button
	if (!Button()) return 0;
	for (i = 1; i <= qty; i++)
	{
		GetItemMark( menu, i, &mark );
		if (mark) return i;
	}
	return 0;
}

//===================================
// draw one of the color chips
static void DrawColorSquare( const RGBColor color, SInt16 squareNumber, SInt16 hSquareQty,
							SInt16 left, SInt16 top)
{
	Rect		squareR;
	RGBColor	blackCol = {0,0,0};
	
	GetSquareRect( &squareR, squareNumber, hSquareQty, left, top );
	RGBForeColor( &blackCol );
	FrameRect( &squareR );
	InsetRect( &squareR, 1, 1 );
	RGBForeColor( &color );
	PaintRect( &squareR );
}

//===================================
// get the rectangle for any color chip
static void GetSquareRect( Rect* squareR, SInt16 squareNumber, SInt16 hSquareQty,
							SInt16 left, SInt16 top )
{
	SInt16		hIndex = squareNumber % hSquareQty;
	SInt16		vIndex = squareNumber / hSquareQty;

	squareR->left = left + (hIndex * (gColorMenuGlobals.squareSize + gColorMenuGlobals.spaceBetween));
	squareR->top = top + (vIndex * (gColorMenuGlobals.squareSize + gColorMenuGlobals.spaceBetween));
	squareR->right = squareR->left + gColorMenuGlobals.squareSize;
	squareR->bottom = squareR->top + gColorMenuGlobals.squareSize;	
}

//===================================
// draw the selection frame for any color chip
static void DrawItem( SInt16 inItem, Boolean selected, SInt16 hSquareQty, SInt16 left,
						SInt16 top)
{
	Rect		squareR;
	RGBColor	wht = {56797,56797,56797};
	
	GetSquareRect( &squareR, inItem, hSquareQty, left, top );
	// for aesthetics, change the chip's black frame to white
	// when selecting it - change it back to black when turning off the selection
	if (selected) RGBForeColor( &wht );
	else ForeColor( blackColor );
	FrameRect( &squareR );
	InsetRect( &squareR, -gColorMenuGlobals.selectionSize, -gColorMenuGlobals.selectionSize );
	if (selected) ForeColor( blackColor );
	else RGBForeColor( &wht );
	PenSize( gColorMenuGlobals.selectionSize, gColorMenuGlobals.selectionSize );
	FrameRect( &squareR );
	ForeColor( blackColor );
	PenSize( 1, 1 );
}

//===============================
static void local_Dimensions( SInt16 inNItems, SInt16* outNCols, SInt16* outNRows )
{
	SInt16	nRows, nCols;
	
	nCols = sqrt( (float)inNItems );
	nRows = inNItems / nCols;
	if (inNItems % nCols) nRows++;
	*outNCols = nCols;
	*outNRows = nRows;
}

#if TARGET_API_MAC_CARBON
//===============================
static void local_FindItemMessage( MenuRef inMenu, Rect* inMenuRect, Point inWhere,
									MenuTrackingDataPtr ioTrackingData )
{
	SInt16		nItems, left, top, nCols, nRows;
	
	nItems = CountMenuItems( inMenu );
	local_Dimensions( nItems, &nCols, &nRows );
	left = inMenuRect->left + gColorMenuGlobals.spaceBetween;
	top = inMenuRect->top + gColorMenuGlobals.spaceBetween;
	ioTrackingData->itemUnderMouse = ioTrackingData->itemSelected =
		PointToItem( inMenu, inWhere, left, top );
	GetSquareRect( &ioTrackingData->itemRect, ioTrackingData->itemUnderMouse - 1, nCols,
					left, top );
	// I don't know what these are for...
	ioTrackingData->virtualMenuTop = 1;
	ioTrackingData->virtualMenuBottom = nRows;
}


//===============================
static void local_HiliteItemMsg( MenuRef inMenu, Rect* inMenuRect, HiliteMenuItemDataPtr inHiliteData )
{
	SInt16		nItems, left, top, nCols, nRows;
	
	nItems = CountMenuItems( inMenu );
	local_Dimensions( nItems, &nCols, &nRows );
	left = inMenuRect->left + gColorMenuGlobals.spaceBetween;
	top = inMenuRect->top + gColorMenuGlobals.spaceBetween;
	if (inHiliteData->previousItem) DrawItem( inHiliteData->previousItem - 1, false, nCols, left, top );
	if (inHiliteData->newItem) DrawItem( inHiliteData->newItem - 1, true, nCols, left, top );
}
#endif

//===============================
// set the given menu item to the given color
void CMSetMenuItemColor( MenuRef menu, SInt16 itemNumber, RGBColor color )
{
	Str31	colorStr;
	
	CMColorToPstr( color, colorStr );
	SetMenuItemText( menu, itemNumber, colorStr );
}

// return the color for the given menu item
void CMGetMenuItemColor( MenuRef menu, SInt16 itemNumber, RGBColor* color )
{
	Str255	colorStr;
	
	GetMenuItemText( menu, itemNumber, colorStr );
	CMPstrToColor( colorStr, color );
}

// convert the given formatted 12 byte pstring into an RGBColor
// the pstring should be in the form RRRRGGGGBBBB where
// RRRR, GGGG, and BBBB are each ASCII four byte hex numbers
void CMPstrToColor( Str31 str, RGBColor* color )
{
	color->red = _HexToNum( str[4] );
	color->red += _HexToNum( str[3] ) * 16;
	color->red += _HexToNum( str[2] ) * 256;
	color->red += _HexToNum( str[1] ) * 4096;
	
	color->green = _HexToNum( str[8] );
	color->green += _HexToNum( str[7] ) * 16;
	color->green += _HexToNum( str[6] ) * 256;
	color->green += _HexToNum( str[5] ) * 4096;
	
	color->blue = _HexToNum( str[12] );
	color->blue += _HexToNum( str[11] ) * 16;
	color->blue += _HexToNum( str[10] ) * 256;
	color->blue += _HexToNum( str[9] ) * 4096;
}

// convert the given RGBColor into a 12 byte formatted pstring
void CMColorToPstr( RGBColor color, Str31 str )
{
	UInt8	n;
	
	str[0] = 12;
	n = (color.red >> 12) & 0x0F;
	str[1] = _NumToHex(n);
	n = (color.red >> 8) & 0x0F;
	str[2] = _NumToHex(n);
	n = (color.red >> 4) & 0x0F;
	str[3] = _NumToHex(n);
	n = color.red & 0x0F;
	str[4] = _NumToHex(n);
	n = (color.green >> 12) & 0x0F;
	str[5] = _NumToHex(n);
	n = (color.green >> 8) & 0x0F;
	str[6] = _NumToHex(n);
	n = (color.green >> 4) & 0x0F;
	str[7] = _NumToHex(n);
	n = color.green & 0x0F;
	str[8] = _NumToHex(n);
	n = (color.blue >> 12) & 0x0F;
	str[9] = _NumToHex(n);
	n = (color.blue >> 8) & 0x0F;
	str[10] = _NumToHex(n);
	n = (color.blue >> 4) & 0x0F;
	str[11] = _NumToHex(n);
	n = color.blue & 0x0F;
	str[12] = _NumToHex(n);
}
