//
//  GSGeometrieHelper.h
//
//  Created by Georg Seifert on 2009-12-01.
//  Copyright (c) 2009 schriftgestaltung.de. All rights reserved.
//

#import <Foundation/NSGeometry.h>
#import <Foundation/Foundation.h>

typedef NS_ENUM(uint8_t, GSAlignment) {
	GSTopLeft = 6,
	GSTopCenter = 7,
	GSTopRight = 8,
	GSCenterLeft = 3,
	GSCenterCenter = 4,
	GSCenterRight = 5,
	GSBottomLeft = 0,
	GSBottomCenter = 1,
	GSBottomRight = 2,
} ;


typedef NS_ENUM(uint8_t, GSIntersectionState) {
	ONTOP = 1,
	PARALLEL = 2
} ;

struct GSPoint3 {
	CGFloat x;
	CGFloat y;
	CGFloat z;
};
typedef struct GSPoint3 GSPoint3;

NS_INLINE GSPoint3 GSMakePoint(CGFloat x, CGFloat y, CGFloat z) {
	GSPoint3 p;
	p.x = x;
	p.y = y;
	p.z = z;
	return p;
}

#if defined(__LP64__) && __LP64__
#define	GSRound(Value)			lround(Value)
#else
#define	GSRound(Value)			(NSInteger)lroundf(Value)
#endif

#ifndef GSGeometrie_Helpers
#define GSGeometrie_Helpers

static inline NSPoint GSUnitVector(NSPoint A);

static inline CGFloat GSCross(NSPoint A, NSPoint B) {
	return A.x * B.y - B.x * A.y;
}

static inline NSPoint GSRoundHandle(NSPoint point) {
	NSPoint Unit = GSUnitVector(point);
	NSPoint Temp1 = point;
	Temp1.x += Unit.x * 0.4;
	Temp1.y += Unit.y * 0.4;
	NSPoint Temp2 = point;
	Temp2.x -= Unit.x * 0.4;
	Temp2.y -= Unit.y * 0.4;
	
	NSPoint Temp0;
	Temp0.x = (CGFloat) round(point.x);
	Temp0.y = (CGFloat) round(point.y);
	CGFloat C0 = GSCross(Temp0, point);
	Temp1.x = (CGFloat) round(Temp1.x);
	Temp1.y = (CGFloat) round(Temp1.y);
	CGFloat C1 = GSCross(Temp1, point);
	Temp2.x = (CGFloat) round(Temp2.x);
	Temp2.y = (CGFloat) round(Temp2.y);
	CGFloat C2 = GSCross(Temp2, point);
	if (point.x * point.y > 0) {
		if (C0 < C1 && C0 < C2) {
			return Temp0;
		}
		if (C1 < C0 && C1 < C2) {
			return Temp1;
		}
		return Temp2;
	}
	else {
		if (C0 > C1 && C0 > C2) {
			return Temp0;
		}
		if (C1 > C0 && C1 > C2) {
			return Temp1;
		}
		return Temp2;
	}
}

static inline NSPoint GSRoundPoint(NSPoint point) {
	point.x = (CGFloat) round(point.x);
	point.y = (CGFloat) round(point.y);
	return point;
}

static inline NSSize GSRoundSize(NSSize size) {
	size.width = (CGFloat) round(size.width);
	size.height = (CGFloat) round(size.height);
	return size;
}

static inline NSPoint GSMaxPoint(NSPoint p1, NSPoint p2) {
	return NSMakePoint((CGFloat) fmax(p1.x, p2.x), (CGFloat) fmax(p1.y, p2.y));
}

static inline NSPoint GSMinPoint(NSPoint p1, NSPoint p2) {
	return NSMakePoint((CGFloat) fmin(p1.x, p2.x), (CGFloat) fmin(p1.y, p2.y));
}

static inline NSRect GSScaleRect(NSRect rect, CGFloat scale) {
	rect.origin.x *= scale;
	rect.origin.y *= scale;
	rect.size.height *= scale;
	rect.size.width *= scale;
	return rect;
}

static inline NSSize GSScaleSize(NSSize aSize, CGFloat Scale) {
	aSize.width *= Scale;
	aSize.height *= Scale;
	return aSize;
}

static inline NSPoint GSScalePoint(NSPoint P, CGFloat scalar) {
	return NSMakePoint(P.x * scalar, P.y * scalar);
}

static inline NSPoint GSAddPoints(NSPoint P1, NSPoint P2) {
	return NSMakePoint(P1.x + P2.x, P1.y + P2.y);
}

static inline NSPoint GSSubtractPoints(NSPoint A, NSPoint B) {
	A.x -= B.x;
	A.y -= B.y;
	return A;
}
static inline NSPoint GSUnitVector(NSPoint A) {
	CGFloat Length = sqrtf((float) ((A.x * A.x) + (A.y * A.y)));
	A.x /= Length;
	A.y /= Length;
	return A;
}

static inline NSPoint GSUnitVectorFromTo(NSPoint B, NSPoint A) {
	A.x -= B.x;
	A.y -= B.y;
	CGFloat Length = sqrtf((float) ((A.x * A.x) + (A.y * A.y)));
	A.x /= Length;
	A.y /= Length;
	return A;
}

static inline NSPoint GSNormalVector1(NSPoint A);

static inline NSPoint GSNormalVector1(NSPoint A) {
	return NSMakePoint(A.y, -A.x);
}

static inline NSPoint GSNormalVector2(NSPoint A);

static inline NSPoint GSNormalVector2(NSPoint A) {
	return NSMakePoint(-A.y, A.x);
}

#define	GSSquareDistance(A, B) (((A.x - B.x) * (A.x - B.x)) + ((A.y - B.y) * (A.y - B.y)))
#define	GSDistance(A, B) sqrtf( (float)(((A.x - B.x) * (A.x - B.x)) + ((A.y - B.y) * (A.y - B.y))))
#define	GSDistance3(A, B) sqrtf( (float)(((A.x - B.x) * (A.x - B.x)) + ((A.y - B.y) * (A.y - B.y)) + ((A.z - B.z) * (A.z - B.z))))

static inline CGFloat GSSlope(NSPoint P1, NSPoint P2) {
	if (fabs(P2.x - P1.x) < 0.001) return NSNotFound;
	return (P2.y - P1.y) / (P2.x - P1.x);
}

NSRect GSRectFromTwoPoints(NSPoint a, NSPoint b);
NSRect GSRectFromFourPoints(NSPoint a, NSPoint b, NSPoint c, NSPoint d);
#endif

CGFloat GSAngleOfVector(NSPoint P);

CGFloat GSAngleBetweenVectors(NSPoint firstPoint, NSPoint secondPoint);


BOOL GSOffsetLine(NSPoint* P0, NSPoint* P1, CGFloat OffsetX, CGFloat OffsetY);

BOOL GSOffsetLineAuto(NSPoint* P0, NSPoint* P1, CGFloat OffsetX, CGFloat OffsetY, CGFloat Position, CGFloat BottomBorder, CGFloat TopBorder);

/*****
*
*   dot
*
*****/
CGFloat GSDot(NSPoint P1, NSPoint P2);

BOOL GSPointIsLeftOfLine(NSPoint P0, NSPoint P1, NSPoint aPoint);

BOOL GSPointIsLeftOfCurve(NSPoint aPoint, NSPoint p0, NSPoint p1, NSPoint p2, CGFloat MinBounds, NSPoint p3);

/*****
*
*   lerp
*
*****/
NSPoint GSLerp(NSPoint P1, NSPoint P2, CGFloat t);

GSPoint3 GSLerp3D(GSPoint3 A, GSPoint3 B, CGFloat t);

NSPoint GSMiddlePoint(NSPoint P1, NSPoint P2);

NSPoint GSNormalize(NSPoint P);

/*****
*
*   gte - greater than or equal
*
*****/
BOOL GSGte(NSPoint P1, NSPoint P2);

#ifndef GLYPHS_VIEWER

NSArray* GSIntersectBezier3Bezier3Segments(NSPoint* P, NSPoint* Q, int Depth);

NSArray* GSIntersectBezier3Bezier3(const NSPoint a1, const NSPoint a2, const NSPoint a3, const NSPoint a4, const NSPoint b1, const NSPoint b2, const NSPoint b3, const NSPoint b4, int Depth);

/** Finds all intersections between a curve and a line.

@param p1 start point of the curve.
@param p2 first offcuve point of the curve.
@param p3 second offcuve point of the curve.
@param p4 end point of the curve.
@param a1 first point of the line.
@param a2 second point of the line.
@return an NSArray of point values for all intersections.
*/
NSArray* GSIntersectBezier3Line(NSPoint p1, NSPoint p2, NSPoint p3, NSPoint p4, NSPoint a1, NSPoint a2);

NSArray* GSIntersectBezier3LineTimes(NSPoint p1, NSPoint p2, NSPoint p3, NSPoint p4, NSPoint a1, NSPoint a2, BOOL checkBounds);

NSArray* GSIntersectBezier3LineUnlimited(NSPoint p1, NSPoint p2, NSPoint p3, NSPoint p4, NSPoint a1, NSPoint a2);

NSArray* GSIntersectBezier3LineUnlimitedTimes(NSPoint p1, NSPoint p2, NSPoint p3, NSPoint p4, NSPoint a1, NSPoint a2);

CGFloat GSDistanceOfPointFromLine3D(GSPoint3 inPoint, GSPoint3 a, GSPoint3 b);

NSPoint GSIntersectLineLine(const NSPoint a1, const NSPoint a2, const NSPoint b1, const NSPoint b2);

NSPoint GSIntersectLineLineUnlimited(const NSPoint aa, const NSPoint ab, const NSPoint ba, const NSPoint bb);

#endif

NSPoint GSNearestPointOnCurveSegmentBounds(NSPoint P, NSPoint* V, CGFloat* tValue);

NSPoint GSNearestPointOnCurveSegment(NSPoint P, NSPoint* V, CGFloat* tValue);

CGFloat GSDistanceOfPointFromCurve(NSPoint inPoint, NSPoint p1, NSPoint p2, NSPoint p3, NSPoint p4);

CGFloat GSSquareDistanceOfPointFromCurveSegment(NSPoint inPoint, NSPoint *V);

CGFloat GSDistanceOfPointFromLineSegment(NSPoint inPoint, NSPoint a, NSPoint b);

CGFloat GSDistanceOfPointFromLine(NSPoint inPoint, NSPoint a, NSPoint b);

CGFloat GSDistanceOfPointFromRect(NSPoint inPoint, NSRect aRect);

NSPoint GSNearestPointOnCurve(NSPoint inPoint, NSPoint p1, const NSPoint p2, const NSPoint p3, const NSPoint p4, CGFloat* t);

NSPoint GSNearestPointOnLineSegment(NSPoint inPoint, NSPoint a, NSPoint b, CGFloat* t);

NSPoint GSNearestPointOnLine(NSPoint inPoint, NSPoint a, NSPoint b, CGFloat* t);

GSPoint3 GSNearestPointOnLine3D(GSPoint3 inPoint, GSPoint3 a, GSPoint3 b, CGFloat* t);

/******
*
* Bezier dividing
*
******/
void GSHalveSegment(NSPoint P0, NSPoint P1, NSPoint P2, NSPoint P3, NSPoint* q0, NSPoint* q1, NSPoint* q2, NSPoint* q3, NSPoint* r1, NSPoint* r2, NSPoint* r3);

void GSDividSegment(NSPoint P0, NSPoint P1, NSPoint P2, NSPoint P3, NSPoint* q0, NSPoint* q1, NSPoint* q2, NSPoint* q3, NSPoint* r1, NSPoint* r2, NSPoint* r3, CGFloat t);
void GSSegmentBetweenPoints(NSPoint P0, NSPoint P1, NSPoint P2, NSPoint P3, NSPoint* q0, NSPoint* q1, NSPoint* q2, NSPoint* q3, NSPoint StartRangePoint, NSPoint EndRangePoint);
NSPoint GSPointAtTime(NSPoint P0, NSPoint P1, NSPoint P2, NSPoint P3, CGFloat t);

CGFloat GSLengthOfSegment(NSPoint P0, NSPoint P1, NSPoint P2, NSPoint P3);

CGFloat GSTForDistance(CGFloat Distance, NSPoint P0, NSPoint P1, NSPoint P2, NSPoint P3, CGFloat maxT);

void GSExtremTimesOfBezierUnlimited(NSPoint p1, NSPoint p2, NSPoint p3, NSPoint p4, CGFloat* t1, CGFloat* t2, CGFloat* t3, CGFloat* t4);

void GSExtremTimesOfBezier(NSPoint p1, NSPoint p2, NSPoint p3, NSPoint p4, CGFloat* t1, CGFloat* t2, CGFloat* t3, CGFloat* t4);

void GSExtremPointsOfBezier(NSPoint p1, NSPoint p2, NSPoint p3, NSPoint p4, NSPoint* r1, NSPoint* r2, NSPoint* r3, NSPoint* r4);

NSArray* GSExtremsOfBezier(NSPoint p1, NSPoint p2, NSPoint p3, NSPoint p4);

void GSBoundsOfBezier(NSPoint p1, NSPoint p2, NSPoint p3, NSPoint p4, CGFloat* minX, CGFloat* minY, CGFloat* maxX, CGFloat* maxY);

NSArray* GSComputeInflexion(NSPoint p1, NSPoint p2, NSPoint p3, NSPoint p4);

NSInteger GSPointSort(id value1, id value2, void* reverse);

void GSTransformComponents(NSAffineTransformStruct M, CGFloat* sX, CGFloat* sY, CGFloat* R);
