//
//  GSPathSegment.h
//  PathOperations
//
//  Created by Georg Seifert on 03.03.14.
//  Copyright (c) 2014 Georg Seifert. All rights reserved.
//

#import <GlyphsCore/GSNode.h>
#import <GlyphsCore/GSPenProtocol.h>

@class FTPointArray;

NS_ASSUME_NONNULL_BEGIN

#define GS_PATH_SEGMENT_ELEMENT_CAPACITY 40

typedef enum : uint8 {
	GSSegmentOptionNone = 0,
	GSSegmentOptionWhite = 1 << 1,
	GSSegmentOptionOpenPath = 1 << 2,
	GSSegmentOptionStartSmooth = 1 << 3,
	GSSegmentOptionEndSmooth = 1 << 4,
	GSSegmentOptionHasSimpleBounds = 1 << 5,
	GSSegmentOptionIsFlat = 1 << 6,
} GSSegmentOptions;

typedef struct GSSegmentStruct {
	NSPoint elements[GS_PATH_SEGMENT_ELEMENT_CAPACITY];
	uint8 count;
	GSNodeType type;
	GSSegmentOptions options;
	NSRect bounds;
} GSSegmentStruct;

void _GSSegmentUpdateBounds(GSSegmentStruct *S);

void _GSSegmentIntersectionPoints(GSSegmentStruct *segment1, GSSegmentStruct *segment2, BOOL unlimited, GSIntersectResultsStruct *intersections);

NSPoint _GSSegmentNearestPoint(GSSegmentStruct *s, NSPoint p, CGFloat *t);

void _GSSegmentSplitAtTime(GSSegmentStruct* s, CGFloat t, GSSegmentStruct* firstHalf, GSSegmentStruct *secondHalf);

double GSCalculateErrorLength(double curveLength, double inputError);

@interface GSPathSegment : NSObject <NSCopying> {

  @public
	int flag;
	GSNodeType type;
	uint8 count;
	GSSegmentOptions options;
	NSPoint elements[GS_PATH_SEGMENT_ELEMENT_CAPACITY];
	NSRect bounds;
}

@property (nonatomic) GSNodeType type;
@property (nonatomic) uint8 countOfPoints;
@property (nonatomic, assign) int flag;
@property (nonatomic) GSSegmentOptions options;
@property (strong, nonatomic, nullable) NSArray *objects;
@property (strong, nonatomic, nullable) NSDictionary<GSShapeKey, id> *attributes;

- (instancetype)initWithLinePoint1:(NSPoint)P1 point2:(NSPoint)P2 options:(GSSegmentOptions)options;
- (instancetype)initWithCurvePoint1:(NSPoint)P1 point2:(NSPoint)P2 point3:(NSPoint)P3 point4:(NSPoint)P4 options:(GSSegmentOptions)options;
- (instancetype)initWithQuartPoint1:(NSPoint)P1 point2:(NSPoint)P2 point3:(NSPoint)P3 point4:(NSPoint)P4 point5:(NSPoint)P5 options:(GSSegmentOptions)options;
- (instancetype)initWithDescription:(NSString *)string;
- (instancetype)initWithQuadratic:(NSArray *)points options:(GSSegmentOptions)options;
- (instancetype)initWithQuadratic:(const NSPoint * __counted_by(count))points count:(int)count options:(GSSegmentOptions)options;
- (instancetype)initWithSegmentStruct:(GSSegmentStruct *)S;
- (GSSegmentStruct)segmentStruct;
- (NSRect)bounds;
- (void)updateBounds;
- (NSComparisonResult)compare:(GSPathSegment *)otherObject italicAngle:(CGFloat)italicAngle;
- (NSComparisonResult)comparePoints:(GSPathSegment *)otherObject;
- (NSComparisonResult)compareMinX:(GSPathSegment *)otherObject;
- (void)reverse;
- (void)roundToGridLength:(CGFloat)gridLength;
- (NSComparisonResult)compare:(GSPathSegment *)otherObject;
- (BOOL)isEqualToSegment:(GSPathSegment *)otherSegment;
- (BOOL)isEqualToSegment:(GSPathSegment *)otherSegment tolerance:(CGFloat)tolerance;
- (BOOL)isEqualToSegmentPoints:(GSPathSegment *)otherSegment;
- (NSPoint)middlePoint;
- (NSPoint)middlePointRight;
#if 0
- (NSPoint)middlePointLeft;
#endif
- (NSPoint)lastPoint;
- (void)splitAtTime:(CGFloat)t firstHalf:(out GSPathSegment * _Nonnull * _Nonnull)left secondHalf:(out GSPathSegment * _Nonnull * _Nonnull)right;

- (void)shortenToTime:(CGFloat)t;
- (void)shortenFromTime:(CGFloat)t;

- (NSPoint)nearestPoint:(NSPoint)p time:(CGFloat *)t;

- (FTPointArray *)intersectionPoints:(GSPathSegment *)otherSegment GSDeprecated("intersectionPoints:unlimited:intersections:");

- (nullable FTPointArray *)intersectionPoints:(GSPathSegment *)otherSegment unlimited:(BOOL)unlimited GSDeprecated("intersectionPoints:unlimited:intersections:");

- (BOOL)intersectionPoints:(GSPathSegment *)otherSegment unlimited:(BOOL)unlimited intersections:(GSIntersectResultsStruct*)intersections;

- (nullable NSArray *)inflectionTimes;

/// returns the "t" if there is one inflection, two inflections are rare and too degenerated, returns NSNotFound if none
- (CGFloat)inflectionTime;

/// similar to inflectionTime but filters out value close the the edges
- (CGFloat)meaningFullInflectionTime;

- (NSPoint)pointAtIndex:(NSUInteger)idx;
- (void)setPoint:(NSPoint)p atIndex:(NSUInteger)idx;
- (CGFloat)curvatureAtTime:(CGFloat)t;
- (CGFloat)signedCurvatureAtTime:(CGFloat)t;
- (NSPoint)pointAtTime:(CGFloat)t;
- (NSPoint)normalAtTime:(CGFloat)t;
- (CGFloat)tangentAngleAtTime:(CGFloat)t;
- (NSPoint)tangentAtTime:(CGFloat)t;
- (CGFloat)tForDistance:(CGFloat)distance;
- (nullable NSArray *)extremePoints NS_RETURNS_RETAINED;
- (nullable NSArray *)extremeTimes NS_RETURNS_RETAINED;
- (CGFloat)length;
- (CGFloat)lengthFast;
- (BOOL)normalizeHandles;
- (void)drawInPen:(NSObject<GSPenProtocol> *)pen;
- (NSString *)descriptionWithPrecision:(uint8_t)precision;

/**
 The angle the segment is spanning.
 a quarter circle would be 90° ...
 */
- (CGFloat)angleSpan;
/**
 is used in makeCorner and offset paths to connect the segments when there is a gap

 @param otherSegment otherSegment
 @param force if YES, it used a less precise geometry
 @param miterFactor the miter limit, default: 10 (like Illustrator, default in SVG is 4)
 @param newPoint on return, the position of the connection
 @return returns YES if a connection was found
 */
- (BOOL)connectWithSegment:(GSPathSegment *)otherSegment force:(BOOL)force miterFactor:(CGFloat)miterFactor newPoint:(out nullable NSPoint *)newPoint;

#ifndef GLYPHS_VIEWER

- (nullable GSPathSegment *)lineSegmentCurveError:(CGFloat)curveError NS_RETURNS_RETAINED;

- (nullable GSPathSegment *)oneQuadraticSegmentCurveError:(CGFloat)curveError NS_RETURNS_RETAINED;

- (nullable GSPathSegment *)twoQuadraticSegmentCurveError:(CGFloat)curveError t:(CGFloat)t NS_RETURNS_RETAINED;

- (GSPathSegment *)threeQuadraticSegmentCurveError:(CGFloat)curveError t:(CGFloat)t NS_RETURNS_RETAINED;

- (GSPathSegment *)fourQuadraticSegmentCurveError:(CGFloat)curveError t:(CGFloat)tMid NS_RETURNS_RETAINED;

- (GSPathSegment *)fiveQuadraticSegmentCurveError:(CGFloat)curveError t:(CGFloat)tMid NS_RETURNS_RETAINED;

- (nullable GSPathSegment *)segmentForInflectionError:(CGFloat)curveError NS_RETURNS_RETAINED;

- (GSPathSegment *)convertToQuadraticWithCurveError:(CGFloat)curveError NS_RETURNS_RETAINED;
#endif

- (nullable id)attributeForKey:(NSString *)key;

- (void)setAttribute:(nullable id)value forKey:(NSString *)key;

@end

NS_ASSUME_NONNULL_END
