//
//  GSLayer.h
//  Glyphs
//
//  Created by Georg Seifert on 13.8.07.
//  Copyright 2007 schriftgestaltung.de. All rights reserved.
//

#import <Cocoa/Cocoa.h>
#import <GlyphsCore/GSContainerProtocol.h>
#import <GlyphsCore/GSFont.h> // for GSWritingDirection
#import <GlyphsCore/GSSelectableElementProtocol.h>
#import <GlyphsCore/GSUserDataProtocol.h>
#import <GlyphsCore/MGOrderedDictionary.h>

#ifdef GLYPHS_VIEWER
#define GSSaveBezierPath NSBezierPath
#endif

NS_ASSUME_NONNULL_BEGIN

@interface NSIndexPath (addIndex)

- (NSIndexPath *)indexPathByAddingOneToLastIndex;

- (nullable NSIndexPath *)indexPathBySubtractingOneToLastIndex;

- (NSUInteger)lastIndex;

@end

typedef struct _GSGlyphMetrics {
	CGFloat width;
	CGFloat ascender;
	CGFloat descender;
	CGFloat italicAngle;
	CGFloat vertWidth;
	CGFloat slantHeight;
	CGFloat topHeight;		// top alignment like xHeight or CapHeight
	CGFloat bottomHeight;	// descender
} GSGlyphMetrics;

typedef enum : NSInteger {
	GSTopMetric = -2,
	GSLeftMetric = -1,
	GSRightMetric = 1,
	GSBottomMetric = 2,
	GSWidthMetric = 3,
	GSVertWidthMetric = 4,
	GSVertOriginMetric = 5,
} GSMetricNumbers;

/*
typedef enum : unsigned short {
	GSLayerTypeMaster = 0,
	GSLayerTypeBackup = 1,
	GSLayerTypeIntermediate = 2,
	GSLayerTypeAlternate = 3,
	GSLayerTypeColor = 4,
	GSLayerTypeColorLayer = 5,
	GSLayerTypeiColor = 6,
	GSLayerTypeSvg = 7,
} GSLayerType;
 */

extern NSString *const GSLayerAttributeAxisRules;
extern NSString *const GSLayerAttributeCoordinates;
extern NSString *const GSLayerAttributeColorPalette;
extern NSString *const GSLayerAttributeSbixSize;
extern NSString *const GSLayerAttributeColor;
extern NSString *const GSLayerAttributeSVG;
extern NSString *const GSLayerAttributeFreeMaster;
@class GSGlyph;
@class GSShape;
@class GSPath;
@class GSNode;
@class GSAnchor;
@class GSComponent;
@class GSHint;
@class GSElement;
@class GSFontMaster;
@class GSMetricValue;
@class GSBackgroundImage;
@class GSGuide;
@class GSAnnotation;
@class GSPathSegment;
@class FTPointArray;
@class GSProxyShapes;
@protocol GSPenProtocol;

#ifndef GLYPHS_VIEWER
@class GSBackgroundLayer;
#endif


/** The class defining the layer object */
@interface GSLayer : NSObject <NSCoding, NSCopying, GSContainerProtocol, GSUserDataProtocol> {
	NSString *_name;
	NSMutableArray<GSShape *> *_shapes;
	MGOrderedDictionary<NSString *, GSAnchor *> *_anchors;
#ifndef GLYPHS_VIEWER
	GSBackgroundLayer *_background;
#endif
	NSMutableDictionary *_userData;
#ifndef GLYPHS_VIEWER
	NSMutableOrderedSet<GSSelectableElement *> *_selection;
#endif
	GSGlyph *__weak _glyph;
	NSBezierPath *_bezierPath;
	NSBezierPath *_bezierPathFast;
	NSBezierPath *_openBezierPath;
	NSBezierPath *_selectionPath;
	NSBezierPath *_secondaryPath;
#ifndef GLYPHS_LITE
	FTPointArray *_intersections;
#endif
	CGFloat _gridLength;
	NSInteger _numberOfPaths;
	NSInteger _numberOfComponents;

	NSString *_layerId;
	NSString *_associatedMasterId;
	NSMutableArray<GSHint *> *_hints;
	GSBackgroundImage *_backgroundImage;
	NSMutableArray<GSGuide *> *_guides;
	BOOL _disableUpdates;
	BOOL _ignoreUpdates;
	BOOL _hasDisabledUpdates;
	NSMutableSet *_changedKeys;

	NSString *_leftMetricsKey;
	NSString *_widthMetricsKey;
	NSString *_rightMetricsKey;
	NSString *_topMetricsKey;
	NSString *_vertWidthMetricsKey;
	NSString *_vertOriginMetricsKey;
	NSString *_bottomMetricsKey;
	BOOL _visible;
#ifdef DEBUG_
	CGFloat _a;
	CGFloat _b;
	CGFloat _penAngle;
#endif
	NSMutableArray<GSAnnotation *> *_annotations;
	GSLayer *_cachedDecomposedCopy;
	NSMutableDictionary *_lsbHeightCache;
	NSMutableDictionary *_rsbHeightCache;
	NSMutableDictionary *_tsbHeightCache;
	NSMutableDictionary *_bsbHeightCache;
  @public
	CGFloat _LSB, _RSB, _width;
	CGFloat _TSB, _BSB;
	CGFloat _vertWidth;
	CGFloat _vertOrigin;

	BOOL _hasAlignedWidth;
	BOOL _isAligned;
	NSColor *_color;
	GSLabelColors _colorIndex;
	os_unfair_lock _alignLock;
	dispatch_semaphore_t _alignSemaphore;

}

- (void)defaultInit;
/// @name Info

@property (nonatomic, unsafe_unretained, readonly, nullable) id content;

/** Returns the containing GSGlyph object */
@property (weak, nonatomic, nullable) GSGlyph *parent;

/** Returns the containing GSFont object */
@property (readonly, nonatomic, nullable) GSFont *font;

/** A pointer to the containing GSGlyph object */
@property (readonly, nonatomic, nullable) GSGlyph *glyph;

/** The title of the layer.

 for master layers this is the name of the associated FontMaster, otherwise it is a custom string (e.g. a date to keep track of history).
 */
@property (strong, nonatomic, nullable) NSString *name;

- (void)initLock;

- (void)disableLock;

- (void)lockReadShapes;

- (void)unlockReadShapes;

- (void)lockWriteShapes;

- (void)unlockWriteShapes;

- (void)lockReadMetrics;
- (void)unlockReadMetrics;
- (void)lockWriteMetrics;
- (void)unlockWriteMetrics;

- (void)lockReadAlignInfo;
- (void)unlockReadAlignInfo;
- (void)lockWriteAlignInfo;
- (void)unlockWriteAlignInfo;

@property BOOL isUpdatingMetrics;

- (NSString *)nameUI;

- (NSString *)ownName;

- (NSArray<GSAxis *> *)axes;

- (nullable NSString *)descriptionForAttributeAxisRules;

- (nullable NSString *)descriptionForAttributeCoordinates;

- (nullable NSString *)descriptionForAttributeColor;

- (nullable NSString *)keyForAttributeCoordinates;

//@property (readonly, nonatomic) NSString *typeName;
//
//- (GSLayerType)typeForName:(NSString *)typeName;

@property (readonly, nonatomic, nullable) NSString *layerKey;

- (void)resetLayerKey;

@property (readonly, nonatomic, nullable) NSString *layerGroupKey;

/// The NSColor object of the color label
@property (strong, nonatomic, nullable) NSColor *color;

/** Returns the index of the color label

 @return 0–11, anything else means no label
 */
@property (assign, nonatomic) GSLabelColors colorIndex;

#ifndef GLYPHS_VIEWER
/**
 If the layer is visible

 This is controlled from the Layer panel
 */
@property (nonatomic) BOOL visible;

/** Returns the content of the object to store it in pList.

 This is used to store the data in the .glyphs file.
 @param format the version of the dict
 */
- (NSDictionary *)propertyListValueFormat:(GSFormatVersion)format;
#endif
/** The layer key is used to access the layer in the glyphs layer dictionary.

 For master layers this should be the id of the FontMaster.
 It could look like this: "FBCA074D-FCF3-427E-A700-7E318A949AE5"
 */
@property (strong, nonatomic) NSString *layerId;

/** The associatedMasterId is used to connect a layer which is not a master layer to a FontMaster.

 This is needed for copies of layers (e.g. for different versions) to access vertical metrics stored in the FontMaster.
 It could look like this: "FBCA074D-FCF3-427E-A700-7E318A949AE5"
 */
@property (strong, nonatomic, nullable) NSString *associatedMasterId;
#ifndef GLYPHS_VIEWER

@property (readonly, nonatomic, nullable) NSDictionary *kerning;

/// @name Metrics

/** The left side bearing.
 @see RSB
 @see width
 */
@property (nonatomic) CGFloat LSB;

/** The right side bearing.
 @see LSB
 @see width
 */
@property (nonatomic) CGFloat RSB;

- (CGFloat)metricsValue:(GSMetricNumbers)side atHeight:(NSInteger)height;

- (CGFloat)lsbAtHeight:(NSInteger)height;
- (void)setLsb:(CGFloat)newValue atHeight:(NSUInteger)atHeight;
- (CGFloat)rsbAtHeight:(NSInteger)height;
- (void)setRsb:(CGFloat)newValue atHeight:(NSUInteger)atHeight;

- (CGFloat)tsbAtHeight:(NSInteger)height;
- (void)setTsb:(CGFloat)newValue atHeight:(NSUInteger)atHeight;
- (CGFloat)bsbAtHeight:(NSInteger)height;
- (void)setBsb:(CGFloat)newValue atHeight:(NSUInteger)atHeight;

#endif

/** The width.
 @see LSB
 @see RSB
 */
@property (nonatomic, assign) CGFloat width;
#ifndef GLYPHS_VIEWER

/** The top side bearing */
@property (nonatomic, assign) CGFloat TSB;

/** The bottom side bearing */
@property (nonatomic, assign) CGFloat BSB;

/** The vertical width */
@property (nonatomic, assign) CGFloat vertWidth;

/**
 Holds an offset from the top of the glyph (ascender) to be applied in vertical typesetting.

 Used with vertWidth.
 */
@property (nonatomic) CGFloat vertOrigin;

/** YES if the layer has automatically aligned spacing */
@property (readonly) BOOL hasAlignedWidth;

/** YES if the layer is automatically aligned */
@property (nonatomic, readonly) BOOL isAligned;

- (BOOL)hasAlignedSideBearings;

@property BOOL isAligning;

/** The metrics key for the left side bearing */
@property (nonatomic, strong, nullable) NSString *leftMetricsKey;

/** The metrics key for the width */
@property (nonatomic, strong, nullable) NSString *widthMetricsKey;

/** The metrics key for the right side bearing */
@property (nonatomic, strong, nullable) NSString *rightMetricsKey;

/** The metrics key for the top side bearing */
@property (nonatomic, strong, nullable) NSString *topMetricsKey;

/** The metrics key for the bottom side bearing */
@property (nonatomic, strong, nullable) NSString *bottomMetricsKey;

/** The metrics key for the vertical width */
@property (nonatomic, strong, nullable) NSString *vertWidthMetricsKey;

/** The metrics key for the vertical origin */
@property (nonatomic, strong, nullable) NSString *vertOriginMetricsKey;

- (CGFloat)metricsValue:(GSMetricNumbers)metric forKey:(NSString *)key;
- (BOOL)metricsValue:(GSMetricNumbers)metric forKey:(NSString *)key value:(CGFloat *)value atHeight:(NSInteger *)atHeight;
+ (BOOL)metricsKey:(NSString *)key name:(NSString *_Nullable *_Nonnull)glyphName operator:(char *)aOperator value:(CGFloat *)value oppositSite:(short *)oppositSite atHeight:(NSInteger *)atHeight;
- (void)moveContent:(NSPoint)offset;
#endif

/// @name Bezier Handling

/** Returns the bezierPath for the layer. Ignores open paths.
 @see openBezierPath
 */
@property (nonatomic, strong, nullable) NSBezierPath *bezierPath;

- (NSBezierPath *)bezierPathFast;

/** Returns the bezierPath for open paths in the layer */
@property (nonatomic, strong, nullable) NSBezierPath *openBezierPath;

@property (nonatomic, strong, nullable) NSBezierPath *selectionPath;

/**
 The bezierPath for drawing the layer. It includes the paths and components.
 */
//@property (nonatomic, strong, nullable)
- (NSBezierPath *)drawBezierPath NS_RETURNS_RETAINED;

- (NSBezierPath *)drawBezierPath:(NSMutableSet *)seenComponents hasPaths:(BOOL *)hasPaths NS_RETURNS_RETAINED;
/**
 The openBezierPath for drawing the layer. It includes the paths and components.
 */
@property (nonatomic, strong, null_resettable) NSBezierPath *drawOpenBezierPath;

@property (nonatomic, strong) NSBezierPath *secondaryPath;

@property (nonatomic, strong, nullable) NSMutableArray *extraHandles;

- (void)applyHints:(CGFloat)scale;

- (NSArray<NSDictionary<NSString *, id> *> *)drawStack;

- (NSArray<NSDictionary<NSString *, id> *> *)drawStackForShapes:(NSArray *)shapes transform:(nullable NSAffineTransform *)transform penClass:(Class)penClass secondaryPen:(nullable NSObject<GSPenProtocol> *)secondPen extraHandles:(nullable NSMutableArray *)extraHandles;

- (NSMutableArray *)shapesGroupedByAttribute:(NSArray *)shapes hasMask:(BOOL *)hasMask;

/** if there is one node selected, return it, otherwise return nil */
@property (nonatomic, unsafe_unretained, readonly, nullable) GSNode *selNode;

@property (nonatomic, strong) NSMutableArray<GSAnnotation *> *annotations;

/** initializes a layer with a dictionary loaded from a pList.

@param layerDict A dictionary
@param formatVersion the format version
*/
- (instancetype)initWithDict:(NSDictionary *)layerDict format:(GSFormatVersion)formatVersion;

#ifndef GLYPHS_VIEWER
- (BOOL)saveToFile:(FILE *)file format:(GSFormatVersion)formatVersion options:(GSSaveOptions)options error:(NSError **)error;
#endif

#ifndef GLYPHS_LITE
/** Returns a string to easily compare the paths of the layers. Layers are compatible if they have the same compareString.

 The string has a letter for each node, all components and all anchor names

 Offcurve = o, Curve = c, Line = l.

 @return An NSString.
 */
- (NSString *)compareString;
#endif

- (BOOL)isEqualToLayer:(GSLayer *)other;

- (NSTimeInterval)lastOperation:(NSMutableSet *)seenComponents;

- (NSTimeInterval)lastOperation;

- (BOOL)isUpToDate;

@property BOOL locked;

@property (nonatomic, readonly, getter=isMasterLayer) BOOL masterLayer;

#ifndef GLYPHS_LITE
@property (nonatomic, readonly, getter=isSpecialLayer) BOOL specialLayer;

@property (nonatomic, readonly, getter=isAnyColorLayer) BOOL anyColorLayer;

@property (nonatomic, getter=isBraceLayer) BOOL braceLayer;

@property (nonatomic, getter=isBracketLayer) BOOL bracketLayer;

@property (nonatomic, getter=isAppleColorLayer) BOOL appleColorLayer;

@property (nonatomic, getter=isSVGColorLayer) BOOL SVGColorLayer;

@property (nonatomic, getter=isColorPaletteLayer) BOOL colorPaletteLayer;

@property (nonatomic, getter=isFullColorLayer) BOOL fullColorLayer;

@property (nonatomic, getter=isSmartComponentLayer, readonly) BOOL smartComponentLayer;

- (BOOL)axesValuesList:(CGFloat *)axesValuesList fontAxes:(NSArray *)axes;

- (NSDictionary *)differenceToLayer:(GSLayer *)otherLayer options:(int)options;

#endif

#ifndef GLYPHS_VIEWER
- (GSNode *)nodeAtPoint:(NSPoint)aPoint excludeNode:(nullable GSNode *)exclude ignoreLocked:(BOOL)ignoreLocked tolerance:(CGFloat)tolerance;

- (nullable GSNode *)nodeAtPoint:(NSPoint)aPoint excludeNodes:(nullable NSSet *)excludes traverseComponents:(BOOL)traverseComponents ignoreLocked:(BOOL)ignoreLocked tolerance:(CGFloat)tolerance;
#endif

#pragma mark Shapes

/// @name Shapes

/** An array of GSShape objects */
@property (strong, nonatomic, null_resettable) NSArray<GSShape *> *shapes;

- (void)setShapesFast:(NSArray<GSShape *> *)shapes;

- (void)setShapes:(NSArray *)shapes hints:(NSArray *)hints;

- (void)getCopyOfContentFromLayer:(GSLayer *)shadowLayer doSelection:(BOOL)doSelection;

/** Returns the count of paths in the layer.

 @return The count of paths in the layer.
 */
- (NSUInteger)countOfShapes;

- (void)insertObject:(GSShape *)shape inShapesAtIndex:(NSInteger)index;

/** Add a path to the layer

 The path will be added to the end of the layer’s paths array.

 @param shape A GSShape object.
 */
- (void)addShape:(GSShape *)shape;

- (void)addShapes:(NSArray<GSShape *> *)shapes;

- (void)addShapeFast:(GSShape *)shape;

/// this is only for iterating over all paths, including recursion over groups.
@property (nonatomic, readonly) GSProxyShapes *paths;
/// this is only for iterating over all components, including recursion over groups.
@property (nonatomic, readonly) GSProxyShapes *components;

/// this will recurse into groups and such
- (void)enumerateShapesUsingBlock:(void (^)(GSShape *obj, NSIndexPath *indexPath, BOOL *stop))block;

- (void)enumeratePathsUsingBlock:(void (^)(GSPath *path, NSIndexPath *indexPath, BOOL *stop))block;

- (void)enumeratePathsWithOptions:(NSEnumerationOptions)opts usingBlock:(void (^)(GSPath *path, NSIndexPath *indexPath, BOOL *stop))block;

- (void)enumerateComponentsUsingBlock:(void (^)(GSComponent *component, NSIndexPath *indexPath, BOOL *stop))block;

- (void)enumerateComponentsWithOptions:(NSEnumerationOptions)opts usingBlock:(void (^)(GSComponent *component, NSIndexPath *indexPath, BOOL *stop))block;

#ifndef GLYPHS_VIEWER
/// Removes the path from the layer.
/**
This is necessary for scripting over Distributed Object (JSTalk)
@param shape The Shape to remove from the layer.
*/
- (void)removeShape:(GSShape *)shape;

- (void)removeShapes:(NSArray<GSShape *> *)shapes;

- (void)removeObjectFromShapesAtIndex:(NSUInteger)index;

- (void)removeShapesAtIndexes:(NSIndexSet *)indexes;

- (void)replaceShapeAtIndex:(NSUInteger)idx withShape:(GSShape *)shape;

- (void)getPositionsFromLayer:(GSLayer *)otherLayer;

- (nullable GSNode *)connectPathsWithNode:(GSNode *)moveNode andNode:(GSNode *)overNode;

- (nullable GSNode *)connectPathsWithNode:(GSNode *)moveNode andNode:(GSNode *)overNode extendPath:(BOOL)extendPath;

/// returns the node before the opening.
- (nullable GSNode *)dividePathAtNode:(GSNode *)node;

- (void)reconnectNodesAtNode1:(GSNode *)node1 node2:(GSNode *)node2 offset:(CGFloat)offset;

- (BOOL)reconnectNodes:(NSArray<GSNode *> *)originNodes;

- (void)connectAllOpenPaths;

- (void)closeOpenPaths:(NSArray *)paths;

/** Is called from element objects (anchors, components) if it has changed.

 @param element The changed element
 */
- (void)elementDidChange:(nullable GSElement *)element;

#endif

/** Returns the count of paths in the layer.

 @return The count of paths in the layer.
 */
- (NSUInteger)countOfPaths;

/** Number of components in the layer. */
@property (nonatomic, readonly) NSUInteger countOfComponents;

- (NSUInteger)indexOfObjectInShapes:(GSShape *)shape;

// compatibility
- (NSUInteger)indexOfPath:(GSShape *)shape;

/** Returns the shape at index.

 @param idx An index within the bounds of the the paths array.
 @return A GSShape object.
 */
- (nullable GSShape *)objectInShapesAtIndex:(NSUInteger)idx;

/** Returns the shape at index.
 similar to objectInShapesAtIndex: but without locking. 
 @param idx An index within the bounds of the the paths array.
 @return A GSShape object.
 */
- (GSShape *)objectInShapesAtIndexFast:(NSUInteger)idx;

- (GSShape *)objectInShapesAtIndexPath:(NSIndexPath *)indexPath;

/** the index path of the shape.

 it is a indexPath to allow access to nested groups
 */
- (nullable NSIndexPath *)indexPathOfShape:(GSShape *)shape;

- (nullable GSNode *)nodeAtIndexPath:(NSIndexPath *)indexPath;

- (nullable NSIndexPath *)indexPathOfNode:(GSNode *)node;

//- (NSInteger)indexOfNode:(GSNode *)node;

- (nullable GSPathSegment *)segmentAtIndexPath:(NSIndexPath *)indexPath;

- (NSArray *)sortedSelectedNodes;

#pragma mark Anchors

/// @name Anchors

/** An array of GSAnchor objects */
@property (strong, nonatomic, null_resettable) NSDictionary<NSString *, GSAnchor *> *anchors;

/** The count of the anchors */
- (NSInteger)countOfAnchors;

#ifndef GLYPHS_VIEWER

- (void)setAnchorsFast:(MGOrderedDictionary *)anchors;
/** Adds the Anchor to the Layer.
 @param anchor A Anchor.
 */
- (void)addAnchor:(GSAnchor *)anchor;

- (void)setAnchorArray:(NSArray *)anchors;

- (void)setAnchorArrayFast:(NSArray *)anchors;

/** Removes the anchor from the Layer.
 @param anchor A GSAnchor object.
 */
- (void)removeAnchor:(GSAnchor *)anchor;

/** Removes the anchor with the name AnchorName from the layer.
 @param anchorName An anchor name.
 */
- (void)removeAnchorWithName:(NSString *)anchorName;
#endif

- (void)switchAnchorFastName:(NSString *)name1 withName:(NSString *)name2;

/** Returns the corresponding anchor for an anchor name.
 @param name The name of the anchor, e.g. "top"
 @return The GSAnchor object for the anchor name.
 */
- (nullable GSAnchor *)anchorForName:(NSString *)name;

/** Returns a GSAnchor with for the given name.
 @param name The name of the anchor
 @param checkComponents If it should look into components
 @return The anchor or nil
 @see anchorNamesTraversingComponents
 */
- (nullable GSAnchor *)anchorForName:(nullable NSString *)name traverseComponents:(BOOL)checkComponents;

/** All anchor names from nested components
 @return A list of anchor names
 */
- (NSArray<NSString *> *)anchorNamesTraversingComponents;
- (NSArray<NSString *> *)anchorNamesTraversingComponentsStopAtComponent:(GSComponent *)stopComponent;

- (NSArray<GSAnchor *> *)anchorsTraversingComponents;

- (NSDictionary <NSString *, GSAnchor *>*)anchorDictTraversingComponents;

+ (NSString *)anchorName:(NSString *)currAnchorKey forScale:(NSPoint)scale;

- (void)anchorChangedName:(GSAnchor *)anchor;

/** Decomposes only the anchors.*/
- (void)copyAnchorsFromComponents;

- (void)decomposeCorners;
- (void)decomposeSmartOutlines;

- (BOOL)flattenOutlines;

- (BOOL)flattenOutlinesRemoveOverlap:(BOOL)removeOverlap origHints:(nullable NSArray *)origHints secondaryPath:(nullable NSBezierPath *)secondaryPath extraHandles:(nullable NSMutableArray *)extraHandles error:(NSError **)error;

/** Builds the compound glyph from the components suggested in GlyphData.xml (decompose attribute).
 */
- (void)makeComponents;

- (nullable NSString *)findComponent:(NSString *)accentName baseName:(NSString *)baseName withSuffix:(nullable NSString *)suffix;

/** A GSLayer copy with all components decomposed.

 @return The layer containing only paths.
 @see decomposeComponent:
 @see decomposeComponentDontNotify:
 */
- (GSLayer *)copyDecomposedLayer;

/** Decomposes components.

 This is not registering undo and KVO. So only use e.g. on export
 This will also effect nested components.
 */

- (void)decomposeComponentsDontNotify;

- (BOOL)decomposeComponentsDontNotifyCallback:(nullable BOOL (^)(GSComponent *component, GSLayer *__nonnull layer))layerCallback;

/** Decomposes components.

 This will also effect nested components.
 */

- (void)decomposeComponents;

- (BOOL)decomposeComponentsCallback:(nullable BOOL (^)(GSComponent *component, GSLayer *__nonnull layer))layerCallback;

/** Decomposes the Component.

 @param component The component to decompose
 */
- (void)decomposeComponent:(GSComponent *)component;

/** Decomposes the Component but does not trigger a UI update

 This is helpful in places where you just need the result but not need to redraw automatically

 @param aComponent The component to decompose
 */
- (void)decomposeComponentDontNotify:(GSComponent *)aComponent;

- (BOOL)decomposeComponentDontNotify:(GSComponent *)aComponent callback:(nullable BOOL (^)(GSComponent *component, GSLayer *__nonnull layer))layerCallback;

/** An array containing names of the components in the layer

 @warning Setting this replaces all components in the layer.
 */
@property (strong, nonatomic) NSArray<NSString *> *componentNames;

/** An array containing names of the components in the layer, including nested components.
 */
@property (strong, nonatomic, readonly) NSArray<NSString *> *componentNamesTraversingComponents;

- (NSArray *)componentNamesTraverseComponents:(BOOL)traverseComponents;
#pragma mark Background

/// @name Background

#ifndef GLYPHS_VIEWER
/** The background layer for this layer */
@property (strong, nonatomic, nullable) GSLayer *background;

- (BOOL)hasBackground;

#endif
#ifndef LIBCORE
/** Moves objects to the background.

 @param checkSelection Only selected objects if YES.
 @param keep           Replaces background layer if NO.
 */
- (void)contentToBackgroundCheckSelection:(BOOL)checkSelection keepOldBackground:(BOOL)keep;

/** Moves objects from the layer to its background layer and vice versa.
 */
- (void)swapForegroundWithBackground;
#endif

/** Empties the layer.
 */
- (void)clear;

#pragma mark BackgroundImage

/// @name Image

/** The image placed in the layer */
@property (strong, nonatomic, nullable) GSBackgroundImage *backgroundImage;

@property (nonatomic, readonly) BOOL hasBackgroundImage;

/** Add the image at the given URL as layer image.

 It verifies if it really is an Image.
 @param URL The Image URL
 */
- (void)addBackgroundImageWithURL:(NSURL *)URL;

#pragma mark Guides

/// @name Guides

/** An array containing all GSGuide objects of the layer */
@property (strong, nonatomic) NSArray<GSGuide *> *guides;

/** Number of guides in the layer.

 @return NSUInteger containing the count of local guides.
 */
- (NSUInteger)countOfGuides;

/** Returns the guide located at idx.

 @param index An index within the bounds of the the guides array.
 @return A GSComponent object.
 */
- (nullable GSGuide *)objectInGuidesAtIndex:(NSUInteger)index;

- (NSUInteger)indexOfObjectInGuides:(nonnull GSGuide *)guide;

- (void)insertObject:(GSGuide *)obj inGuidesAtIndex:(NSUInteger)index;

- (void)addGuide:(GSGuide *)guide;

- (void)removeObjectFromGuides:(GSGuide *)guide;

- (void)removeObjectFromGuidesAtIndex:(NSUInteger)index;

#pragma mark Hints

/// @name Hints

/** The array of hints */
@property (nonatomic, strong, null_resettable) NSArray<GSHint *> *hints;

/** The array of hints but might be `nil` and the setter is not registering undo and notify the layer of changes. So use it only for setting up the layer, not during the lifetime of the layer */
@property (nonatomic, strong, nullable) NSMutableArray<GSHint *> *hintsFast;

/** The number of hints in the layer
 @return Count of hints.
 */
- (NSUInteger)countOfHints;

- (NSUInteger)indexOfObjectInHints:(nonnull GSHint *)hint;

@property (nonatomic, readonly) BOOL hasHints;
@property (nonatomic, readonly) BOOL hasTrueTypeHints;
@property (nonatomic, readonly) BOOL hasPostScriptHints;
@property (nonatomic, readonly) BOOL hasCorners;

- (NSUInteger)countOfPostScriptHints;
/** Adds the Hint to the Layer.

 @param hint A GSHint.
*/
- (void)addHint:(GSHint *)hint;

/// checks if a hint with the same type and index path is already present.
- (void)addHintCheck:(GSHint *)hint;

/// Adds a copy of the Hint to the Layer.
/**
@param Hint A Hint.
@return The copied Hint.
*/
//- (GSHint*) addHintCopy:(GSHint*) Hint ;

/** Returns the GSHint at the specified index.

 @param idx An index within the bounds of the hints array.

 @return The GSHint object for the specified hint index.
 */
- (nullable GSHint *)objectInHintsAtIndex:(NSUInteger)idx;

- (void)insertObject:(id)hint inHintsAtIndex:(NSUInteger)idx;

/** Removes the Hint from the Layer.

 @param hint A Hint.
*/
- (void)removeHint:(GSHint *)hint;

- (void)removeObjectFromHintsAtIndex:(NSUInteger)idx;

- (void)updateCorners;

#pragma mark Annotations

/// @name Annotations

- (void)addAnnotation:(GSAnnotation *)anAnnotation;

- (void)removeAnnotation:(GSAnnotation *)anAnnotation;

- (NSUInteger)countOfAnnotations;

#ifndef GLYPHS_VIEWER

//- (void)getAnnotations:(id*)buffer range:(NSRange)inRange;

- (nullable GSAnnotation *)objectInAnnotationsAtIndex:(NSUInteger)idx;

//- (void)insertObject:(GSAnnotation*)aGSAnnotation inAnnotationsAtIndex:(NSUInteger)idx;
//- (void)insertAnnotations:(NSArray*)_annotationArray atIndexes:(NSIndexSet*)indexes;

- (void)removeObjectFromAnnotationsAtIndex:(NSUInteger)idx;

//- (void)removeAnnotationsAtIndexes:(NSIndexSet*)indexes;
//- (void)replaceObjectInAnnotationsAtIndex:(NSUInteger)idx withObject:(GSAnnotation *)aGSAnnotation;
//- (void)replaceAnnotationsAtIndexes:(NSIndexSet *)indexes withAnnotations:(NSArray *)_annotationArray;
#endif

- (void)saveHints;

- (void)restoreHints;

- (void)updateHints;

- (void)adjustHintsForReversedPaths:(NSArray <GSHint *>*)hints;

#pragma mark TempData
/**
 a  dictionary that stores data. It will not be written to disk.
 */
@property (nonatomic, strong, nullable) NSDictionary *tempData;

/**
 Adds key/value to tempData. Pass nil as value to remove previous set data

 @param value and object or nil
 @param key the key
 */
- (void)setTempData:(nullable id)value forKey:(nonnull NSString *)key;

/**
 return value for key in tempData

 @param key the key
 @return a value or nil
 */
- (nullable id)tempDataForKey:(nonnull NSString *)key;

#pragma mark Kerning

/** @name Kerning */

/** Return the kern value for the sequence LeftLayer + self.

 This checks if there is class kerning or exception.

 @param previousLayer The GSLayer before the current layer

 @warning returns MaxInt if no value is defined for that pair
 @return The kerning value as float or MaxInt if there is no value
 */
- (CGFloat)previousKerningForLayer:(GSLayer *)previousLayer direction:(GSWritingDirection)direction;

/** Return the kern value for the sequence self + RightLayer.

 This checks if there is class kerning or exception.
 @param nextLayer The GSLayer after the current layer
 @warning returns MaxInt if no value is defined for that pair
 @return The kerning value as float or MaxInt if there is no value
*/
- (CGFloat)nextKerningForLayer:(GSLayer *)nextLayer direction:(GSWritingDirection)direction;

/** Sets the kerning for the sequence LeftLayer + self.

 This checks if there is class kerning or exception.
 @param value the kern value as float
 @param previousLayer The GSLayer before the current layer
*/
- (void)setPreviousKerning:(CGFloat)value forLayer:(GSLayer *)previousLayer direction:(GSWritingDirection)direction;

/** Sets the kerning for the sequence self + RightLayer.

 This checks if there is class kerning or exception.
 @param value the kern value as float
 @param nextLayer The GSLayer after the current layer
*/
- (void)setNextKerning:(CGFloat)value forLayer:(GSLayer *)nextLayer direction:(GSWritingDirection)direction;

/** Returns a boolean value if LeftLayer + self has a kerning exception.

 @param previousLayer The GSLayer before the current layer
 @return `True` if there is an exception
*/
- (BOOL)previousKerningExceptionForLayer:(GSLayer *)previousLayer direction:(GSWritingDirection)direction;

/** Sets, if LeftLayer + self has an exception or not.

@param exception	if it is an exception or not
@param previousLayer The layer on the left
*/
- (void)setPreviousKerningException:(BOOL)exception forLayer:(GSLayer *)previousLayer direction:(GSWritingDirection)direction;

/** Returns a boolean value if self + RightLayer has a kerning exception.

@param nextLayer The GSLayer after the current layer
@return `True` if there is an exception
*/
- (BOOL)nextKerningExceptionForLayer:(GSLayer *)nextLayer direction:(GSWritingDirection)direction;

/** Sets, if LeftLayer + self has an exception or not.

@param exception	if it is a exertion or not
@param nextLayer	The layer to the right
*/
- (void)setNextKerningException:(BOOL)exception forLayer:(GSLayer *)nextLayer direction:(GSWritingDirection)direction;

#pragma mark Selection

/// @name Selection

/** An array of GSElement objects.

 All currently selected elements, otherwise nil.
 */
@property (strong, nonatomic, null_resettable) NSOrderedSet<GSSelectableElement *> *selection;

- (NSUInteger)countOfSelection;

#ifndef GLYPHS_VIEWER
- (void)setSelectionUndo:(NSOrderedSet<GSSelectableElement *> *)selection;
/** adds an element to the selection.

@param newSelection The element to add to the selection.
*/

- (void)addSelection:(GSSelectableElement *)newSelection;

- (void)addObjectsFromArrayToSelection:(NSArray<GSSelectableElement *> *)objects;

- (void)removeObjectFromSelectionAtIndex:(NSUInteger)idx;

- (void)removeObjectFromSelection:(GSSelectableElement *)object;

- (void)removeObjectsFromSelection:(NSArray<GSSelectableElement *> *)objects;

- (void)clearSelection;

- (void)clearSelectionUndo;

- (BOOL)selectionContainsObject:(GSSelectableElement *)object;

#endif
@property NSTimeInterval lastUpdate;
#ifndef GLYPHS_VIEWER
#pragma mark updates

/// @name Updating

@property (nonatomic, getter=areUpdatesDisabled) BOOL disableUpdates;

- (void)ignoreUpdates;

- (BOOL)isIgnoringUpdates;

/** Disables updating
 @warning Always make sure to call startUpdates
 @see startUpdates
 */
- (void)stopUpdates;

/** Enables updating and combined notifies for all changes that occurred while disabled.
 @see stopUpdates
 @see enableFutureUpdates
 */
- (void)startUpdates;

/** Enables updating but doesn’t notify for past changes
 @see stopUpdates
 @see startUpdates
 */
- (void)enableFutureUpdates;

/// Is called from an object after it changed the outline of the layer.
- (void)setNeedUpdateShapes;
#endif

/// marks the RSB and LSB to need update.
- (void)setNeedUpdateMetrics;

#ifndef GLYPHS_VIEWER
- (CGFloat)syncMetricsThreshold:(GSMetricNumbers)side;

/// Recalculates the LSB and RSB using the metrics keys.
- (void)syncMetrics;

/// Recalculates the LSB using the metrics keys.
- (void)syncLeftMetrics;

- (void)syncWidthMetrics;

/// Recalculates the RSB using the metrics keys.

- (void)syncRightMetrics;

#ifndef GLYPHS_LITE
- (void)syncTopMetrics;

- (void)syncVertWidthMetrics;

- (void)syncBottomMetrics;

- (void)reinterpolateMetrics;
#endif

- (void)preloadContent;

/// Recalculates the LSB and RSB.
- (void)updateMetrics;

#endif

/** The master to which the layer belongs.

 @return The GSFontMaster to which the layer belongs.
 */
- (nullable GSFontMaster *)associatedFontMaster;

- (nullable GSFontMaster *)associatedFontMasterFast;

@property (nonatomic, readonly) CGFloat ascender;

@property (nonatomic, readonly) CGFloat descender;

@property (nonatomic, readonly) CGFloat italicAngle;

@property (nonatomic, readonly) CGFloat slantHeight;

@property (nonatomic, readonly) CGFloat topHeight;

- (CGFloat)metricForType:(GSMetricsType)type;

- (CGFloat)metricForName:(NSString *)key;
/** Metrics for the Layer

 @return The vertical metrics from the fonts fontMaster with the same layerId/associatedLayerId and the width of the layer.
 */
- (GSGlyphMetrics)glyphMetrics;

- (GSGlyphMetrics)glyphMetricsForDirection:(GSWritingDirection)alignment;

- (NSArray<GSMetricValue *> *)metrics;

- (NSArray<GSMetricValue *> *)metricsForMaster:(GSFontMaster *)master;

/** The bounding box of the layer.

 @return An NSRect encompassing all paths and components in the layer.
 */
- (NSRect)bounds;

/** The bounding box of the layer.

 It only checks the nodes, not the size of the marking shape

 @return An NSRect encompassing all paths and components in the layer.
 */
- (NSRect)fastBounds;

@property (readonly, nonatomic) CGFloat minX;
@property (readonly, nonatomic) CGFloat minY;
@property (readonly, nonatomic) CGFloat maxX;
@property (readonly, nonatomic) CGFloat maxY;

- (NSRect)boundsAngle:(nullable NSAffineTransform *)transform;

#ifndef GLYPHS_VIEWER
#ifndef LIBCORE

/** The bounding box of the current selection.

 @return An NSRect encompassing all selected paths, components and anchors on the layer.
 */
- (NSRect)boundsOfSelection;

- (NSRect)boundsOfSelectionAngle:(nullable NSAffineTransform *)transform;
#endif
/** Tidies up paths in the layer (removes double points, unnecessary nodes, etc.)
 */
- (void)cleanUpPaths;

#ifndef LIBCORE
/** Checks is all nodes have the correct connection setting and corrects them if necessary.
 */
- (void)checkConnections;
#endif

/** Corrects the path directions and reorders the paths in the layer.
 */
- (void)correctPathDirection;

#ifndef LIBCORE
- (NSArray *)checkForCoincidentPaths;
#endif

- (void)roundSelectedCoordinates;

- (void)roundCoordinates;

#ifndef LIBCORE
- (void)roundCoordinatesToGrid:(CGFloat)gridLength;
#endif

- (void)roundCoordinatesToGridFast:(CGFloat)gridLength;

/** Add Extreme Points to all paths.
 */
- (void)addExtremePoints;

/** Add Extreme Points to all paths.

 @param force Forces extremes if YES.
 */
- (void)addExtremePointsForce:(BOOL)force checkSelection:(BOOL)checkSelection;

/** Adds nodes in segment inflections.
 */
- (void)addInflectionPoints;
/**
 Calculates the intersection of all Paths with the line between StartPoint and EndPoint

 @param startPoint the first point
 @param endPoint   the second point

 @return a list of point values
 */
- (NSArray *)calculateIntersectionsStartPoint:(NSPoint)startPoint endPoint:(NSPoint)endPoint;

- (NSArray *)calculateIntersectionsForPath:(GSPath *)path startPoint:(NSPoint)startPoint endPoint:(NSPoint)endPoint;
/**
 Calculates the intersection of all Paths with the line between StartPoint and EndPoint

 @param startPoint the first point
 @param endPoint   the second point
 @param decompose  indicates if components should be intersected, too.
 @param ignoreLocked  ignore locked and out of focus paths
 @return a list of point values
 */
- (NSArray *)calculateIntersectionsStartPoint:(NSPoint)startPoint endPoint:(NSPoint)endPoint decompose:(BOOL)decompose ignoreLocked:(BOOL)ignoreLocked clipToBound:(BOOL)clipToBound;

- (NSArray *)calculateIntersectionsStartPoint:(NSPoint)startPoint endPoint:(NSPoint)endPoint decompose:(BOOL)decompose ignoreLocked:(BOOL)ignoreLocked clipToBound:(BOOL)clipToBound ignoreComponents:(BOOL)ignoreComponents;

#ifndef LIBCORE
#ifndef GLYPHS_LITE
- (FTPointArray *)intersections;
#endif
#endif
#ifndef GLYPHS_VIEWER
/** The undoManager of the parent (GSGlyph).

 @return An NSUndoManager object.
 */
@property (nonatomic, readonly, nullable) NSUndoManager *undoManager;

@property (nonatomic, readonly, nullable) NSUndoManager *undoManagerCheck;

#endif
#endif

/** The glyph to which the layer belongs. Equivalent to the parent property.

 @return The GSGlyph object for the layer.
 */
- (GSGlyph *)glyph;

#ifndef LIBCORE
#ifndef GLYPHS_VIEWER

/// Returns the element dicts of all selected elements
- (NSDictionary *)selectedObjects;

/** used to "paste"

 @param dictionary the dict that originally came from selectedObjects
 @param selectedNodes is needed to attach corners
 */
- (BOOL)pasteDict:(NSDictionary *)dictionary selectedNodes:(NSArray *)selectedNodes error:(NSError **)error;

- (void)selectedPaths:(NSMutableArray *)selectedPaths notSelected:(nullable NSMutableArray *)notSelectedPaths;

+ (nullable NSData *)layerDataFromDict:(NSDictionary *)dict format:(GSFormatVersion)format error:(NSError **)error;

#endif
#endif

#ifndef GLYPHS_VIEWER

/** Transforms all selected elements.

 This applies the Transform to all elements in the selection.
 @param transform the transform.
*/
- (void)transformSelection:(NSAffineTransform *)transform;

- (void)transform:(NSAffineTransform *)transform checkForSelection:(BOOL)checkForSelection;

- (void)transform:(NSAffineTransform *)transform checkForSelection:(BOOL)checkForSelection doComponents:(BOOL)doComponents; // compatibility with plugins

- (void)transform:(NSAffineTransform *)transform checkForSelection:(BOOL)checkForSelection doComponent:(nullable BOOL (^)(GSComponent *__nullable component))componentCallback;

- (void)transformFast:(NSAffineTransform *)transform;

- (void)transformFast:(NSAffineTransform *)transform doComponent:(nullable BOOL (^)(GSComponent *__nullable component))componentCallback;

#ifndef LIBCORE
/** Shifts the layer horizontally

 This OtherShifts dict contains the shift values that will be applied to other glyphs at the same time. This is used to prevent displacement of components if the base glyphs are shifted.

 @param shift       The Shift value.
 @param otherShifts A dict with shift values for the hole shift operation. Keys are glyphs names, value NSNumbers.
 */
- (void)horizontalShift:(CGFloat)shift otherShifts:(nullable NSDictionary *)otherShifts;
#endif
#endif

- (id)copyThin:(BOOL)thin;

- (id)copyThin:(BOOL)thin selection:(BOOL)selection;

/** Draw the layer with a pen object

 @param pen An object that conforms to the GSPenProtocol
 */
- (void)drawInPen:(NSObject<GSPenProtocol> *)pen;

- (void)drawInPen:(NSObject<GSPenProtocol> *)pen openPen:(nullable NSObject<GSPenProtocol> *)openPen;

- (void)drawBlackInPen:(NSObject<GSPenProtocol> *)pen openPen:(nullable NSObject<GSPenProtocol> *)openPen;

- (void)drawColorInPen:(NSObject<GSPenProtocol> *)pen;

- (void)drawDrawStack:(NSArray *)drawStack inPen:(NSObject<GSPenProtocol> *)pen;

@property (nonatomic, strong) NSMutableDictionary *partSelection;

/** The color for the layer.

 This can be a Palette entry (for MS color fonts) or the Master color for color layer fonts

 @param dark If drawing in dark mode
 @return the color as NSColor
 */
- (NSColor *)layerColor:(BOOL)dark;

- (nullable NSColor *)paletteColor:(NSUInteger)paletteIdx;

#pragma mark Attributes

/// @name Attributes
/** Place to store data.

 Here it is possible to store something. Please use a unique key.
 The objects should be able to serialize to a plist. If they are not, they should return all infos to store in description (as String).
 */
@property (strong, nonatomic, nullable) NSDictionary *attributes;

- (NSUInteger)countOfAttributes;
/**
 convenience accessor to get to the content of the attributes dict

 @param key the key
 @return the data stored with key
 */
- (nullable NSObject *)attributeForKey:(NSString *)key;

- (void)setAttributesFast:(NSMutableDictionary *)attributes;

/** Adds something to the layers attributes.

 This also triggers undo/document dirty state.

 @param value The object should be able to serialize to a plist. If they are not, they should return all infos to store in description (as String).
 @param key   Please use a unique key that gives some indication who put it there (e.g. prefix it with your name or project).
 */
- (void)setAttribute:(nullable id)value forKey:(nonnull NSString *)key;

/** Adds something to the layers attributes.
 Does not trigger KVO and undo
 */
- (void)setAttributeFast:(id)value forKey:(NSString *)key;

/** Removed the object with this key.

 @param key The key to remove
 */
- (void)removeAttributeForKey:(NSString *)key;

+ (BOOL)rule:(NSDictionary *)rule matchesCoordinates:(nullable GSAxisValues *)axisValues;

@property (nonatomic, strong) NSDictionary *readBuffer;

- (void)setReadBuffer:(NSObject *)values forKey:(NSString *)key;

- (BOOL)postRead:(NSError **)error format:(GSFormatVersion)formatVersion;

- (BOOL)isSecondaryMetric:(GSMetricValue *)metrics;

@end
NS_ASSUME_NONNULL_END
