//
//  GSPath.h
//  FontBezier Doc
//
//  Created by Georg Seifert on 19.10.05.
//  Copyright 2005 schriftgestaltung.de. All rights reserved.
//

#import <Cocoa/Cocoa.h>

@class GSNode;
@class GSGlyph;
@class GSLayer;
@class GSElement;
@protocol GSPenProtocol;

static inline NSArray* GSMakeCurveSegment(NSPoint P1, NSPoint P2, NSPoint P3, NSPoint P4);

static inline NSArray* GSMakeCurveSegment(NSPoint P1, NSPoint P2, NSPoint P3, NSPoint P4) {
	return @[[NSValue valueWithPoint:P1],
			 [NSValue valueWithPoint:P2],
			 [NSValue valueWithPoint:P3],
			 [NSValue valueWithPoint:P4]];
}

static inline NSArray* GSMakeLineSegment(NSPoint P1, NSPoint P2);

static inline NSArray* GSMakeLineSegment(NSPoint P1, NSPoint P2) {
	return @[[NSValue valueWithPoint:P1],
			 [NSValue valueWithPoint:P2]];
}

/** Possible values retuned by the method */
typedef NS_ENUM(NSInteger, GSPathDirection) {
	///	Counter Clock Wise
	GSCounterClockWise = -1,
	/// Clock Wise
	GSClockWise = 1
};

/** The class defining the path object */

@interface GSPath : NSObject <NSCoding, NSCopying> {
	NSMutableArray* _nodes;
	BOOL _closed;
	GSLayer* __unsafe_unretained _parent;
}
/// A pointer to the containing GSLayer object
@property (unsafe_unretained, nonatomic) GSLayer* parent;

/// Set if the path is closed.
@property (nonatomic) BOOL closed;

/** initializes a path with a dictionary loaded from a pList.
@param pathDict A dictionary
*/
- (instancetype)initWithPathDict:(NSDictionary*)pathDict;

- (instancetype)initWithPathString:(NSString*)pathString;

/// Returns the content of the path to store in pList. 
- (NSDictionary*)pathDict;

- (BOOL)isEqualToPath:(GSPath*)OtherPath;

#ifndef GLYPHS_VIEWER

- (BOOL)saveToFile:(FILE*)File compact:(BOOL)Compact error:(NSError**)error;

/** gives the nearest Point on the path

@param aPoint   the point
@param PathTime On return the "path time" on reference. The integer part is the Index of the next on-curve point. The fractional digits contain the time to the point.

@return the Point on the path
*/
- (NSPoint)nearestPointOnPath:(NSPoint)aPoint pathTime:(CGFloat*)PathTime;

- (NSPoint)pointAtPathTime:(CGFloat)PathTime;

- (GSNode*)nearestNodeWithPathTime:(CGFloat)PathTime;
/** Tests if pt lays on the path and than inserts a new point and corrects the handles of the other points to preserve the paths appearance.

@param PathTime the "path time" the integer part is the Index of the next on-curve point. The fractional digits contain the time to the point.
@return If the new point object, or nil if something went wrong.
*/
- (GSNode*)insertNodeWithPathTime:(CGFloat)PathTime;

#endif

/** @name Nodes */
 
/// An array of GSNode objects.
@property (strong, nonatomic) NSArray* nodes;

/** Returns the count of nodes in the path.
 
 @return The count of nodes in the path.
 */
- (NSUInteger)countOfNodes;

/** Adds the node to the end of the paths array.

@param Node The node to add.
*/
- (void)addNode:(GSNode*)Node;

/** Adds the node without notifying the layer
 
 You need to make sure that the layer is notified
 
 @param Node The node to add.
 */
- (void)addNodeFast:(GSNode*)Node;

/** Inserts a Node at the Index into nodes.

@param Node A GSNode object to insert.
@param Index The index in the receiver at which to insert the Node. This value must not be greater than the count of nodes in the path.
*/
- (void)insertNode:(GSNode*)Node atIndex:(NSInteger)Index;

/**
Adds the nodes from Path to the

@param Path The Path to add.
*/
- (void)appendPath:(GSPath*)Path;

/** removes the point at Index from the path.

@param Index The index of the node to remove.
*/
- (void)removeNodeAtIndex:(NSInteger)Index;

/** Removes Node from path.

@param Node The node to return from the path.
*/
- (void)removeNode:(GSNode*)Node;

#ifndef GLYPHS_VIEWER

/** Removes Node and tries to keep the shape of the path.

e.g. If Node is a off-curve node, it removes the second off-curve node and sets the segment to LINE.
@param Node The node to return from the path.
*/
- (void)removeNodeCheckKeepShape:(GSNode*)Node;
- (void)removeNodeCheckKeepShape:(GSNode*)Node normalizeHandles:(BOOL)NormalizeHandles;
/** Removes Node.

If Node is a off-curve node, it removes the second off-curve node and sets the segment to LINE.
@param Node The node to return from the path.
*/
- (void)removeNodeCheck:(GSNode*)Node;

+ (CGFloat)fitCurveOn1:(NSPoint)On1 off1:(NSPoint*)Off1 Off2:(NSPoint*)Off2 on2:(NSPoint)On2 toOrigiPoints:(NSPoint*)OrigiPoints count:(unsigned short)Count;

+ (CGFloat)fitCurveOn1:(NSPoint)On1 off1:(NSPoint*)Off1 Off2:(NSPoint*)Off2 on2:(NSPoint)On2 toOrigiPoints:(NSPoint*)OrigiPoints count:(unsigned short)Count normalizeHandles:(BOOL)NormalizeHandles;

+ (CGFloat)fitCurveOn1:(NSPoint)On1 off1:(NSPoint*)Off1 Off2:(NSPoint*)Off2 on2:(NSPoint)On2 toPointsQ0:(NSPoint)Q0 q1:(NSPoint)Q1 q2:(NSPoint)Q2;


/**
Is called from node objects if it has changed.

This triggers the update of the interface

@param Element A GSNode object
*/
- (void)elementDidChange:(GSElement*)Element;

#endif

/** Finds the Node that is used as start Node
 */
- (GSNode*)findStartNode;

/** Makes the Node the first in the path.

It checks if the node is in the path. If not, it makes nothing.

@param Node  The node that should be the starting node.
*/
- (void)makeNodeFirst:(GSNode*)Node;


/**
Closes or opens the Path. If necessary it adds or removes points.

@param Closed A BOOL
*/
- (void)setClosePath:(BOOL)Closed;

- (void)setLocked:(GSNode*)Node withPoint:(NSPoint)Point;

- (void)setSmooth:(GSNode*)Node withCenterPoint:(NSPoint)CenterPoint oppositePoint:(NSPoint)OppositePoint;

- (void)setSmooth:(GSNode*)Node withCenterNode:(GSNode*)CenterNode oppositeNode:(GSNode*)OppositeNode NS_DEPRECATED_MAC(10_0, 10_0);

- (void)setLocked:(GSNode*)Node shadow:(GSNode*)ShadowNode withPoint:(NSPoint)aPoint;

- (void)setMirrored:(GSNode*)Node withCenterPoint:(NSPoint)CenterPoint oppositePoint:(NSPoint)OppositePoint ;

/// Fixes the connection in all nodes
- (void)checkConnections;

/** Returns the GSNode located at Index.

 The Index is corrected for overflow and underflow. Index = -1 retunes the last node...

 @param Index An index.
 @return A GSNode object.
*/
- (GSNode*)nodeAtIndex:(NSInteger)Index;

/** Returns the index of the Node in nodes.

@param Node A GSNode object
@return The index of the node in the path.
*/
- (NSUInteger)indexOfNode:(GSNode*)Node;

/// For open paths, returns the last node of the path, otherwise `nil`.
- (GSNode*)endNode;

/// For open paths, returns the first node of the path, otherwise `nil`.
- (GSNode*)startNode;

/** The path direction

 @return -1 for counter clock wise and 1 for clock wise
 */
- (GSPathDirection)direction;

- (BOOL)enclosesPath:(GSPath*)SmallPath;

#ifndef GLYPHS_VIEWER

/// Reverses the path direction
- (void)reverse;

/** Cleans up the Path

 Removes empty line segments
 
 Converts flat curves into line
 
 Rounds coordinates to the gridLenth specified in the font
 */
- (void)cleanUp;

- (void)cleanUpGrid:(CGFloat)Grid;

/// Returns the undoManager of the containing glyph.
- (NSUndoManager*)undoManager;

#endif

/// A pointer to the containing GSGlyph object
- (GSGlyph*)glyph;

/** Returns the bounding box of the receiver’s path.

@return The rectangle that encloses the path of the receiver. If the path contains curve segments, the bounding box encloses the curve but may not enclose the control points used to calculate the curve.
*/
- (NSRect)bounds;

/** Returns the bounding box of the receiver’s path.

It only calculates the bounds from the control points. This is usually to big for curves

@return The rectangle that encloses the path of the receiver. If the path contains curve segments, the bounding box encloses control points.
*/
- (NSRect)fastBounds;

#ifndef GLYPHS_VIEWER

/**
Moves the path by the offset given be Point.

@param Point A NSPoint
*/
- (void)moveWithPoint:(NSPoint)Point;

/**
Adds extremes to the path.

If there is a node closed to actual extreme, it will try to move it. If Force is not set, it does not add extremes if there is not enough space

@param Force Determines if it adds nodes even if there is not enough space and it would add a bump.
*/
- (void)addExtremes:(BOOL)Force;

#endif

/**
Calculates the angle of the tangent at the given node

@param Node	  The node where to calculate the angle
@param Direction -1 for the angle to the previous node, 1 of the next node

@return the angle in degrees
*/
- (CGFloat)tangentAngleAtNode:(GSNode*)Node direction:(int)Direction;

/**
Calculates the angle of the tangent at the node at the given Index

@param Index	  The index of the node where to calculate the angle
@param Direction -1 for the angle to the previous node, 1 of the next node

@return the angle in degrees
*/
- (CGFloat)tangentAngleAtNodeAtIndex:(NSInteger)Index direction:(int)Direction;

- (NSPoint)unitVectorAtNodeAtIndex:(NSInteger)NodeIndex ;
/**
Calculates a bezier path representing the path.

This is a bit expensive call so you might cache the result.

is is probably better to get the bezier path from the layer

@return A NSBezierPath representing the path.
*/
- (NSBezierPath*)bezierPath;

/**
Draws the path using a pen conforming to the GSPenProtocol

@param pen A pen conforming to the GSPenProtocol
*/
- (void)drawInPen:(NSObject <GSPenProtocol>*)pen;

#ifndef GLYPHS_VIEWER

/** Calculates a list of segments

 This might simplify path operations in some cases

 The array contains an array for each segment. The arrays contain 2 or 4 NSValue object. Call `[Value pointValue]` to the the NSPoint object

 @return A NSArray containing NSArrays.
 */
@property (nonatomic, strong) NSArray *segments;

/**
Replaces the nodes of the path by the points from the Segments array.

@param Segments A List of segments.
*/

//#ifdef DEBUG
//
//- (void)flattenDeltas;
//#endif

#endif
@end
