// Copyright 2000
// College of Computer Science
// Northeastern University Boston MA 02115

// This software may be used for educational purposes as long as this copyright
// notice is retained at the top of all files

// Should this software be modified, the words "Modified from Original" must be
// included as a comment below this notice

// All publication rights are retained.  This software or its documentation may
// not be published in any media either in whole or in part.

///////////////////////////////////////////////////////////////////////////////

// GraphicsWidget.cpp

///////////////////////////////////////////////////////////////////////////////

#include "CoreTools.h"
#include "SystemBase.h"
#include "GraphicsWidget.h"


///////////////////////////////////////////////////////////////////////////////

void PolygonWidget::DrawDetails(GraphicsWindow& G) const {

	// GraphicsState interactions
	// uses:	PenSize, PenMode, PenColor, FillColor
	// changes:	

	if (G.WindowState.PenMode == PM_Normal) {

		switch(ImageMethod) {
			case FF_Fill:
			case FF_FillFrame:

				DoFill(G);			// fill interior with fill color

				// break if we just want to fill
				if (ImageMethod == FF_Fill)
					break;

			case FF_Frame:

				DoFrame(G);			// fill exterior with pen color
				break;

			default:

				break;
		} // end switch
	}
	else {
		switch(ImageMethod) {
			case FF_Fill:
			
				DoFill(G);			// fill interior
				break;

			case FF_Frame:

				DoFrame(G);			// fill exterior
				break;

			case FF_FillFrame:

				DoBoth(G);			// fill union of interior and exterior
				break;

			default:

				break;
		}
	}
}


#if defined(CORE_PLATFORM_WIN32)


HRGN PolygonWidget::PolygonInterior(GraphicsWindow& G) const {

	HRGN Region;

	// poly data

	const NativePointData* PolygonPointer
		= (const NativePointData*) Poly.DataPointer();

	// poly size

	int Size = Poly.ValidSize();

	// poly fill mode

	int InternalMode;

	switch(PolyFillMode) {
		
		case PFM_Winding:
			InternalMode = WINDING;
			break;

		case PFM_Regular:
		default:
			InternalMode = ALTERNATE;
			break;
	}

	// create poly region

	Region = CreatePolygonRgn(PolygonPointer, Size, InternalMode);

	return Region;
}


HRGN PolygonWidget::PolygonExterior(GraphicsWindow& G) const {

	int i;
	int Size = Poly.ValidSize();

	HRGN Region = CreateRectRgn(0, 0, 0, 0);
	HRGN Line;

	for (i = 1; i < Size; i++) {
		Line = G.WIN32_Line_Region(Poly.Access(i-1), Poly.Access(i));
		CombineRgn(Region, Region, Line, RGN_OR);
		DeleteObject(Line);
	}

	return Region;
}


HRGN PolygonWidget::CompletePolygon(GraphicsWindow& G) const {
	HRGN Interior = PolygonInterior(G);
	HRGN Exterior = PolygonExterior(G);

	CombineRgn(Interior, Interior, Exterior, RGN_OR);

	DeleteObject(Exterior);

	return Interior;
}

#endif	// end WIN32 specific


void PolygonWidget::DoFill(GraphicsWindow& G) const {

#if defined(CORE_PLATFORM_WIN32)

	HRGN Region = PolygonInterior(G);

	if(Region == 0)
		return;

	G.WIN32_Show_Region(Region, G.NativeState.FillBrush);

	DeleteObject(Region);

#endif	// end WIN32 specific


#if defined(CORE_PLATFORM_MACOS)

#endif	// end MacOS specific

}


void PolygonWidget::DoFrame(GraphicsWindow& G) const {

#if defined(CORE_PLATFORM_WIN32)

	HRGN Region = PolygonExterior(G);

	if(Region == 0)
		return;

	G.WIN32_Show_Region(Region, G.NativeState.PenBrush);

	DeleteObject(Region);

#endif	// end WIN32 specific


#if defined(CORE_PLATFORM_MACOS)

#endif	// end MacOS specific

}


void PolygonWidget::DoBoth(GraphicsWindow& G) const {

#if defined(CORE_PLATFORM_WIN32)

	HRGN Region = CompletePolygon(G);

	if(Region == 0)
		return;

	G.WIN32_Show_Region(Region, G.NativeState.FillBrush);

	DeleteObject(Region);

#endif	// end WIN32 specific


#if defined(CORE_PLATFORM_MACOS)

#endif	// end MacOS specific

}


///////////////////////////////////////////////////////////////////////////////

void RectWidget::DrawDetails(GraphicsWindow& G) const {

	// GraphicsState interactions
	// uses:	PenSize, PenMode, PenColor, FillColor
	// changes:	

	// find rect bounds in order

	RectData S = R;

	S.Order();

	short x1, y1, x2, y2;

	S.Get(x1, y1, x2, y2);

	// define pen size variables

	short xpen, ypen;

	short a1, b1, a2, b2;


#if defined(CORE_PLATFORM_WIN32)

	HRGN Region;
	HRGN InnerRegion;
	HRGN OuterRegion;

	Region = CreateRectRgn(x1, y1, x2, y2);

	switch (ImageMethod) {
		case FF_Fill:
		case FF_FrameFill:

			G.WIN32_Show_Region(Region, G.NativeState.FillBrush);

			// break if we just want to fill
			//  or if pen mode is not normal
			if ((ImageMethod == FF_Fill) || (G.WindowState.PenMode != PM_Normal))
				break;

		case FF_Frame:

			G.WindowState.PenSize.Get(xpen, ypen);

			a1 = x1 + xpen;
			b1 = y1 + ypen;

			a2 = x2 - xpen;
			b2 = y2 - ypen;

			// if pen is very thick then frame might be the same as fill
			// so just fill with the pen brush and break

			if ((a1 >= a2) || (b1 >= b2)) {
				G.WIN32_Show_Region(Region, G.NativeState.PenBrush);
				break;
			}

			// will create doughnut region between two rects

			InnerRegion = CreateRectRgn(a1, b1, a2, b2);

			OuterRegion = CreateRectRgn(0, 0, 0, 0);	// dummy needed to initialize

			CombineRgn(OuterRegion, Region, InnerRegion, RGN_DIFF);

			G.WIN32_Show_Region(OuterRegion, G.NativeState.PenBrush);

			DeleteObject(InnerRegion);

			DeleteObject(OuterRegion);

			break;

		default:

			break;
	}

	DeleteObject(Region);

#endif	// end WIN32 specific


#if defined(CORE_PLATFORM_MACOS)

#endif	// end MacOS specific

}


void OvalWidget::DrawDetails(GraphicsWindow& G) const {

	// GraphicsState interactions
	// uses:	PenSize, PenMode, PenColor, FillColor
	// changes:	

	// find rect bounds in order

	RectData S = R;

	S.Order();

	short x1, y1, x2, y2;

	S.Get(x1, y1, x2, y2);

	// define pen size variables

	short xpen, ypen;

	short a1, b1, a2, b2;


#if defined(CORE_PLATFORM_WIN32)

	HRGN Region;
	HRGN InnerRegion;
	HRGN OuterRegion;

	Region = CreateEllipticRgn(x1, y1, x2, y2);

	switch (ImageMethod) {
		case FF_Fill:
		case FF_FrameFill:

			G.WIN32_Show_Region(Region, G.NativeState.FillBrush);

			// break if we just want to fill
			//  or if pen mode is not normal
			if ((ImageMethod == FF_Fill) || (G.WindowState.PenMode != PM_Normal))
				break;

		case FF_Frame:

			G.WindowState.PenSize.Get(xpen, ypen);

			a1 = x1 + xpen;
			b1 = y1 + ypen;

			a2 = x2 - xpen;
			b2 = y2 - ypen;

			// if pen is very thick then frame might be the same as fill
			// so just fill with the pen brush and break

			if ((a1 >= a2) || (b1 >= b2)) {
				G.WIN32_Show_Region(Region, G.NativeState.PenBrush);
				break;
			}

			// will create doughnut region between two ovals

			InnerRegion = CreateEllipticRgn(a1, b1, a2, b2);

			OuterRegion = CreateRectRgn(0, 0, 0, 0);	// dummy needed to initialize

			CombineRgn(OuterRegion, Region, InnerRegion, RGN_DIFF);

			G.WIN32_Show_Region(OuterRegion, G.NativeState.PenBrush);

			DeleteObject(InnerRegion);

			DeleteObject(OuterRegion);

			break;

		default:

			break;
	}

	DeleteObject(Region);

#endif	// end WIN32 specific


#if defined(CORE_PLATFORM_MACOS)

#endif	// end MacOS specific

}

///////////////////////////////////////////////////////////////////////////////

void PixelWidget::DrawDetails(GraphicsWindow& G) const {

#if defined(CORE_PLATFORM_WIN32)

	::SetPixel(G.hdc, x, y, color.Native());

#endif	// end WIN32 specific

}


PixelWidget& PixelWidget::Peek(GraphicsWindow& G) {

	if(! G.IsOpen())
		return *this;

	G.PrepareGraphics();

#if defined(CORE_PLATFORM_WIN32)

	color = ::GetPixel(G.hdc, x, y);

#endif	// end WIN32 specific

	G.ReleaseGraphics();

	return *this;
}


PixelWidget& PixelWidget::Peek(int index) {
	GraphicsWindow* gwp = GraphicsWindowPtr(index);

	if (gwp)
		return Peek(*gwp);
	else
		return *this;
}

///////////////////////////////////////////////////////////////////////////////

void MoveToWidget::DrawDetails(GraphicsWindow& G) const {

	// GraphicsState interactions
	// uses:	
	// changes:	Location

	G.WindowState.Location.Set(P);

}


void LineWidget::DrawDetails(GraphicsWindow& G) const {

	// GraphicsState interactions
	// uses:	PenSize, PenMode, PenColor [BackgroundMode, BackgroundColor]
	// changes:	Location


#if defined(CORE_PLATFORM_WIN32)

	HRGN Region;

	Region = G.WIN32_Line_Region(P, Q);

	G.WIN32_Show_Region(Region, G.NativeState.PenBrush);

	DeleteObject(Region);

#endif	// end WIN32 specific


#if defined(CORE_PLATFORM_MACOS)

#endif	// end MacOS specific


	// now update the location

	G.WindowState.Location = Q;
}


void LineToWidget::DrawDetails(GraphicsWindow& G) const {

	// GraphicsState interactions
	// uses:	Location, PenSize, PenMode, PenColor [BackgroundMode, BackgroundColor]
	// changes:	Location

	LineWidget Widget(G.WindowState.Location, P);

	// set other widget parameters

	if (IsPermanent())
		Widget.SetPermanent();
	else
		Widget.SetTemporary();

	// now draw

	Widget.Draw(G);
}


void DeltaLineWidget::DrawDetails(GraphicsWindow& G) const {

	// GraphicsState interactions
	// uses:	Location, PenSize, PenMode, PenColor [BackgroundMode, BackgroundColor]
	// changes:	Location

	PointData P = G.WindowState.Location;
	PointData Q = P + Delta;

	LineWidget Widget(P, Q);
	
	// set other widget parameters

	if (IsPermanent())
		Widget.SetPermanent();
	else
		Widget.SetTemporary();

	// now draw

	Widget.Draw(G);
}


void DeltaMoveWidget::DrawDetails(GraphicsWindow& G) const {

	// GraphicsState interactions
	// uses:	Location
	// changes:	Location

	MoveToWidget Widget(G.WindowState.Location + Delta);

	// set other widget parameters

	if (IsPermanent())
		Widget.SetPermanent();
	else
		Widget.SetTemporary();

	// now draw

	Widget.Draw(G);
}

///////////////////////////////////////////////////////////////////////////////

void ClipRectWidget::DrawDetails(GraphicsWindow& G) const {

	// GraphicsState interactions
	// uses:	
	// changes:	ClipRect, EnableClip [in GraphicsState], internal state

#if defined(CORE_PLATFORM_WIN32)

	// give back current clip region

	if (G.WindowState.EnableClip) {
		DeleteObject(G.NativeState.ClipRgn);
		G.NativeState.ClipRgn = NULL;
	};

#endif	// end WIN32 specific


	// make sure new clip rect is ordered

	RectData R = ClipRect;
	R.Order();

	// save clip settings

	G.WindowState.ClipRect   = R;
	G.WindowState.EnableClip = EnableClip;


#if defined(CORE_PLATFORM_WIN32)

	// if clip is enabled then set up new clip region

	if (EnableClip) {
		short x1, y1, x2, y2;
		R.Get(x1, y1, x2, y2);

		G.NativeState.ClipRgn = CreateRectRgn(x1, y1, x2, y2);
	}

#endif	// end WIN32 specific

}

///////////////////////////////////////////////////////////////////////////////

void TransformWidget::DrawDetails(GraphicsWindow& G) const {
	// GraphicsState interactions
	// uses:	
	// changes:	Transform [in GraphicsState]

	G.WindowState.Transform = Transform;
}

///////////////////////////////////////////////////////////////////////////////

void PolygonWidget2D::DrawDetails(GraphicsWindow& G) const {

	// GraphicsState interactions
	// uses:	PenSize, PenMode, PenColor, FillColor
	// changes:

	// create a pixel poly which is the transform of our polygon

	PolygonData PixelPoly;
	G.WindowState.Transform.Apply(Poly, PixelPoly);

	// use the pixel poly to create a polygon widget

	PolygonWidget Widget(PixelPoly);

	// set other widget parameters

	if (IsPermanent())
		Widget.SetPermanent();
	else
		Widget.SetTemporary();


	Widget.SetImageMethod(ImageMethod);

	Widget.SetPolyFillMode(PolyFillMode);

	// now draw

	Widget.Draw(G);
}

///////////////////////////////////////////////////////////////////////////////

void RectWidget2D::DrawDetails(GraphicsWindow& G) const {

	RectWidget Widget(G.WindowState.Transform(R));

	// set other widget parameters

	if (IsPermanent())
		Widget.SetPermanent();
	else
		Widget.SetTemporary();


	Widget.SetImageMethod(ImageMethod);

	// now draw

	Widget.Draw(G);
}


void OvalWidget2D::DrawDetails(GraphicsWindow& G) const {

	OvalWidget Widget(G.WindowState.Transform(R));

	// set other widget parameters

	if (IsPermanent())
		Widget.SetPermanent();
	else
		Widget.SetTemporary();


	Widget.SetImageMethod(ImageMethod);

	// now draw

	Widget.Draw(G);
}

///////////////////////////////////////////////////////////////////////////////

void LineWidget2D::DrawDetails(GraphicsWindow& G) const {

	LineWidget Widget(G.WindowState.Transform(P), G.WindowState.Transform(Q));

	// set other widget parameters

	if (IsPermanent())
		Widget.SetPermanent();
	else
		Widget.SetTemporary();

	// now draw

	Widget.Draw(G);
}


void LineToWidget2D::DrawDetails(GraphicsWindow& G) const {

	LineToWidget Widget(G.WindowState.Transform(P));

	// set other widget parameters

	if (IsPermanent())
		Widget.SetPermanent();
	else
		Widget.SetTemporary();

	// now draw

	Widget.Draw(G);
}


void MoveToWidget2D::DrawDetails(GraphicsWindow& G) const {

	MoveToWidget Widget(G.WindowState.Transform(P));

	// set other widget parameters

	if (IsPermanent())
		Widget.SetPermanent();
	else
		Widget.SetTemporary();

	// now draw

	Widget.Draw(G);
}


void DeltaLineWidget2D::DrawDetails(GraphicsWindow& G) const {

	DeltaLineWidget Widget(G.WindowState.Transform(Delta));

	// set other widget parameters

	if (IsPermanent())
		Widget.SetPermanent();
	else
		Widget.SetTemporary();

	// now draw

	Widget.Draw(G);
}


void DeltaMoveWidget2D::DrawDetails(GraphicsWindow& G) const {

	DeltaMoveWidget Widget(G.WindowState.Transform(Delta));

	// set other widget parameters

	if (IsPermanent())
		Widget.SetPermanent();
	else
		Widget.SetTemporary();

	// now draw

	Widget.Draw(G);
}

///////////////////////////////////////////////////////////////////////////////

void PenSizeWidget::DrawDetails(GraphicsWindow& G) const {

	// GraphicsState interactions
	// uses:	
	// changes:	PenSize [in GraphicsState]

	G.WindowState.PenSize = PenSize;


#if defined(CORE_PLATFORM_WIN32)

	PenSize.Get(G.NativeState.xpen, G.NativeState.ypen);

#endif	// end WIN32 specific

}


void PenModeWidget::DrawDetails(GraphicsWindow& G) const {

	// GraphicsState interactions
	// uses:	
	// changes:	PenMode [in GraphicsState]

	G.WindowState.PenMode = PenMode;
}


void PenColorWidget::DrawDetails(GraphicsWindow& G) const {

	// GraphicsState interactions
	// uses:	
	// changes:	PenColor [in GraphicsState]

	// save pen color

	G.WindowState.PenColor.Set(color);


#if defined(CORE_PLATFORM_WIN32)

	G.NativeState.SetPenColor(color);

#endif	// end WIN32 specific

}


void FillColorWidget::DrawDetails(GraphicsWindow& G) const {

	// GraphicsState interactions
	// uses:	
	// changes:	FillColor [in GraphicsState]

	// save pen color

	G.WindowState.FillColor.Set(color);


#if defined(CORE_PLATFORM_WIN32)

	G.NativeState.SetFillColor(color);

#endif	// end WIN32 specific

}

///////////////////////////////////////////////////////////////////////////////

void TextWidget::DrawDetails(GraphicsWindow& G) const {

	// GraphicsState interactions
	// uses:	Location, TextAlign, TextColor, PenMode
	//			BackgroundMode, BackgroundColor
	// changes:

	// set system parameters for text draw

	G.PrepareText();

	// use the current location to position text

	short x, y;
	G.WindowState.Location.Get(x,y);

	// get pointer to string data and its length

	const char* p = text.c_str();
	int length    = text.length();

#if defined(CORE_PLATFORM_WIN32)

	TextOut(G.hdc, x, y, p, length);

#endif	// end WIN32 specific


#if defined(CORE_PLATFORM_MACOS)

#endif	// end MacOS specific

}


void TextAlignWidget::DrawDetails(GraphicsWindow& G) const {

	// GraphicsState interactions
	// uses:	
	// changes:	TextAlign [in GraphicsState]

	G.WindowState.TextAlign = TextAlign;
	
	
#if defined(CORE_PLATFORM_WIN32)

	// extract vertical and horizontal components
	int valign = TextAlign & VAlign_MASK;
	int halign = TextAlign & HAlign_MASK;

	// will follow the convention that drawing text does not
	// update the current graphics location

	int WIN32align = TA_NOUPDATECP;

	switch (valign) {
		case VAlign_Baseline:
			WIN32align |= TA_BASELINE;

			break;


		case VAlign_Bottom:
			WIN32align |= TA_BOTTOM;

			break;

		// top is default

		case VAlign_Top:
		default:
			WIN32align |= TA_TOP;

			break;
	};

	switch (halign) {
		case HAlign_Center:
			WIN32align |= TA_CENTER;

			break;


		case HAlign_Right:
			WIN32align |= TA_RIGHT;

			break;

		// left is default

		case HAlign_Left:
		default:
			WIN32align |= TA_LEFT;

			break;
	};


	G.NativeState.TextAlign = WIN32align;

#endif	// end WIN32 specific

}


TextAlignWidget& TextAlignWidget::Set(int aligncode) {

	// extract vertical and horizontal components
	int valign = aligncode & VAlign_MASK;
	int halign = aligncode & HAlign_MASK;

	// check for valid v & h align information

	switch (valign) {
		case VAlign_Baseline:
		case VAlign_Bottom:
		case VAlign_Top:
			// valid information so break
			break;

		default:
			// no valid information so use stored setting
			valign = TextAlign & VAlign_MASK;
	};

	switch (halign) {
		case HAlign_Center:
		case HAlign_Right:
		case HAlign_Left:
			// valid information so break
			break;

		default:
			// no valid information so use stored setting
			halign = TextAlign & HAlign_MASK;
	};
	
	TextAlign = valign | halign;

	return *this;
};


void TextColorWidget::DrawDetails(GraphicsWindow& G) const {

	// GraphicsState interactions
	// uses:	
	// changes:	TextColor [in GraphicsState]

	// save pen color

	G.WindowState.TextColor.Set(color);

}

///////////////////////////////////////////////////////////////////////////////

void BackgroundModeWidget::DrawDetails(GraphicsWindow& G) const {

	// GraphicsState interactions
	// uses:	
	// changes:	BackgroundMode [in GraphicsState]

	G.WindowState.BackgroundMode = BackgroundMode;
}


void BackgroundColorWidget::DrawDetails(GraphicsWindow& G) const {

	// GraphicsState interactions
	// uses:	
	// changes:	BackgroundColor [in GraphicsState]

	// save pen color

	G.WindowState.BackgroundColor.Set(color);
}

