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

#import <GlyphsCore/GSContainerProtocol.h>
#import <GlyphsCore/GSFont.h> // for GSWritingDirection
#import <GlyphsCore/GSSaveContext.h>
#import <GlyphsCore/GSNode.h>
#import <GlyphsCore/GSSelectableElementProtocol.h>
#import <GlyphsCore/GSUserDataProtocol.h>
#import <GlyphsCore/MGOrderedDictionary.h>
#import <GlyphsCore/GSDrawSlice.h>
#import <GlyphsCore/GSShapeGroup.h>
#import <GlyphsCore/GSValueStore.h>

#ifdef GLYPHS_VIEWER
typedef NSBezierPath GSSafeBezierPath;
#endif

@class GSPalettes;

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 {
	GSMetricTop = -2,
	GSMetricLeft = -1,
	GSMetricRight = 1,
	GSMetricBottom = 2,
	GSMetricWidth = 3,
	GSMetricVertWidth = 4,
	GSMetricVertOrigin = 5,
} GSMetricNumbers;

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

typedef NSString *GSLayerAttribute NS_TYPED_EXTENSIBLE_ENUM;
extern GSLayerAttribute const GSLayerAttributeAxisRules;
extern GSLayerAttribute const GSLayerAttributeCoordinates;
/// Either the NSString `@"*"` for the text color or an object responding to `-integerValue` with the palette color index.
extern GSLayerAttribute const GSLayerAttributeColorPalette;
extern GSLayerAttribute const GSLayerAttributeSbixSize;
extern GSLayerAttribute const GSLayerAttributeColor;
extern GSLayerAttribute const GSLayerAttributeSVG;

typedef NS_OPTIONS(uint8_t, GSLayerCompareFlags) {
	GSLayerCompareShapes = 1 << 0,
	GSLayerCompareAnchors = 1 << 1,
	GSLayerCompareHints = 1 << 2,
};

static const GSLayerCompareFlags GSLayerCompareAll = GSLayerCompareShapes | GSLayerCompareAnchors | GSLayerCompareHints;

@class GSGlyph;
@class GSShape;
@class GSPath;
@class GSNode;
@class GSAnchor;
@class GSComponent;
@class GSHint;
@class GSElement;
@class GSFontMaster;
@class GSMetricStore;
@class GSBackgroundImage;
@class GSGuide;
@class GSAnnotation;
@class GSPathSegment;
@class FTPointArray;
@class GSProxyShapes;
@protocol GSPenProtocol;

#ifndef GLYPHS_VIEWER
@class GSBackgroundLayer;
#endif

/// The drawing and editing representation of a glyph.
///
/// A **master layer** provides the glyph drawing associated with a font master.
/// An **Intermediate layer** supplies interpolation data between masters.
/// An **Alternative layer** replaces the associated master layer when its axis rules match.
/// **Color layers** provide color-specific representations.
/// **Smart layers** describe variations along glyph-local axes.
/// Additional layers (**backup layers**) hold working or historical drawings without participating in export.
///
/// A layer belongs to a glyph, which in turn belongs to a font.
/// This ownership gives the layer access to font axes, masters, and other glyphs.
/// Detached layers are supported, but APIs that require font or glyph context can return no result or provide only partial behavior.
/// Assign a ``parent`` to a layer without adding it to a glyph to keep it temporary but still connected to a glyph and font.
///
/// Shapes form the graphical content of a layer.
/// Paths describe contours, components reference other glyphs, and shape groups organize shapes hierarchically.
/// Anchors provide named attachment positions, hints store outline instructions and path components, and guides assist editing without forming part of the outline.
///
/// Layer metrics describe the advance and spacing around the graphical content.
/// Horizontal metrics include the width and side bearings, while vertical metrics include the vertical advance, origin, and side bearings.
/// Metrics and metrics keys can depend on the associated font master and related glyphs.
///
/// A layer can produce Bézier paths, emit its content to a pen, or preserve richer paint and composition information in a draw stack.
/// Its grid is derived from the associated master and controls coordinate rounding during editing and geometry operations.
///
/// A layer maintains the current selection and provides editing operations for its shapes and other selectable elements.
/// Many editing methods operate on the selection when one exists and on all applicable content otherwise.
///
/// A layer can also have a background layer as an editable companion and a separate background image as reference.
@interface GSLayer : NSObject <NSCoding, NSCopying, GSContainerProtocol, GSUserDataProtocol>

/// A KVO dependency key for changes to any content of the layer.
///
/// Observe this property to receive combined content changes.
/// Its value is always `nil`.
@property (nonatomic, unsafe_unretained, readonly, nullable) id content;

/// The glyph containing the layer, or `nil` while the layer is detached.
///
/// Setting this property does not add the layer to the glyph.
@property (weak, nonatomic, nullable) GSGlyph *parent;

/// The font containing the layer through its glyph, or `nil` while the layer is detached.
@property (readonly, nonatomic, nullable) GSFont *font;

/// The glyph containing the layer, or `nil` while the layer is detached.
///
/// This property is an alias for ``parent``.
@property (readonly, nonatomic, nullable) GSGlyph *glyph;

/// A custom name for the layer.
///
/// The displayed name of a master, Intermediate, Alternative, or color layer can be derived from its role and attributes instead.
@property (strong, nonatomic, nullable) NSString *name;

/// Enables thread synchronization for a layer created without locks.
- (void)initLock;

/// Disables thread synchronization for the layer.
///
/// Use this only when the layer cannot be accessed concurrently.
- (void)disableLock;

/// Acquires the read lock for shapes.
- (void)lockReadShapes;

/// Releases the read lock for shapes.
- (void)unlockReadShapes;

/// Acquires the write lock for shapes.
- (void)lockWriteShapes;

/// Releases the write lock for shapes.
- (void)unlockWriteShapes;

/// Acquires the read lock for hints.
- (void)lockReadHints;

/// Releases the read lock for hints.
- (void)unlockReadHints;

/// Acquires the write lock for hints.
- (void)lockWriteHints;

/// Releases the write lock for hints.
- (void)unlockWriteHints;

/// Acquires the read lock for metrics.
- (void)lockReadMetrics;

/// Releases the read lock for metrics.
- (void)unlockReadMetrics;

/// Acquires the write lock for metrics.
- (void)lockWriteMetrics;

/// Releases the write lock for metrics.
- (void)unlockWriteMetrics;

/// Acquires the read lock for anchors.
- (void)lockReadAnchors;

/// Releases the read lock for anchors.
- (void)unlockReadAnchors;

/// Acquires the write lock for anchors.
- (void)lockWriteAnchors;

/// Releases the write lock for anchors.
- (void)unlockWriteAnchors;

/// Acquires the read lock for alignment information.
- (void)lockReadAlignInfo;

/// Releases the read lock for alignment information.
- (void)unlockReadAlignInfo;

/// Acquires the write lock for alignment information.
- (void)lockWriteAlignInfo;

/// Releases the write lock for alignment information.
- (void)unlockWriteAlignInfo;

/// Whether the layer is currently updating its side bearings.
@property BOOL isUpdatingMetrics;

/// A name suitable for presenting the layer in the user interface.
///
/// The name is derived from the associated master and layer attributes when applicable.
/// Setting this property changes ``name`` and does not change the associated master or attributes.
@property (strong, nullable) NSString *nameUI;

/// Axes used to locate the layer in its design space.
///
/// Smart glyphs use their glyph-local axes, corner glyphs use the corner axes, and other glyphs use the font axes.
/// The layer must be attached to a font when it does not provide glyph-local or corner axes.
- (NSArray<GSAxis *> *)axes;

/// Axes represented by Intermediate-layer coordinates.
///
/// Smart glyphs include both font axes and glyph-local axes.
- (NSArray<GSAxis *> *)braceCoordinateAxes;

/// Explicit Intermediate-layer coordinates keyed by axis ID.
///
/// Returns an empty dictionary when the layer has no coordinates.
/// Setting an empty dictionary removes the coordinates, so the layer is no longer an Intermediate layer.
@property (strong, nonatomic) NSDictionary<NSString *, NSNumber *> *coordinates;

- (CGFloat)coordinateForAxisId:(NSString *)axisId;

- (void)setCoordinate:(CGFloat)coordinate forAxisId:(NSString *)axisId;

/// Alternative-layer axis rules keyed by axis ID.
///
/// Each rule can contain inclusive `min` and `max` bounds in internal axis values.
/// Returns an empty dictionary when the layer has no axis rules.
/// Setting an empty dictionary removes the axis rules, so the layer is no longer an Alternative layer.
@property (strong, nonatomic) NSDictionary<NSString *, NSDictionary<NSString *, NSNumber *> *> *axisRules;

#ifndef GLYPHS_LITE
/// A display description of the Alternative-layer axis rules.
///
/// Returns `nil` when the axis-rules attribute is missing or invalid.
- (nullable NSString *)descriptionForAttributeAxisRules;

/// A display description of the explicit Intermediate-layer coordinates.
///
/// Missing coordinates are represented by a hyphen.
- (nullable NSString *)descriptionForAttributeCoordinates;

/// A stable description of the color-layer format, or an empty string for a non-color layer.
- (NSString *)descriptionForAttributeColor;

/// A localized description of the color-layer format, or an empty string for a non-color layer.
- (NSString *)localizedDescriptionForAttributeColor;

#ifndef GLYPHS_VIEWER
/// A stable key describing the complete Intermediate-layer location.
///
/// Missing coordinates use the associated master value or the axis default.
/// Returns an empty string when the layer has no coordinates.
- (NSString *)keyForAttributeCoordinates;

/// A display string for an axis rule.
///
/// - Parameter axis: The axis described by the rule.
/// - Parameter rule: Inclusive `min` and `max` bounds in internal axis values.
/// - Returns: A compact rule description, or `nil` when the rule has no bounds.
+ (nullable NSString *)axisRuleString:(GSAxis *)axis rule:(NSDictionary<NSString *, NSNumber *> *)rule;
#endif
#endif

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

/// A derived key that distinguishes the layer by its role and attributes.
///
/// Ordinary layers without a name can have no key.
@property (readonly, nonatomic, nullable) NSString *layerKey;

/// Invalidates the derived layer key after information used to form it changes.
- (void)resetLayerKey;

/// A derived key used to group corresponding special and color layers.
///
/// Layers without grouping attributes can have no group key.
@property (readonly, nonatomic, nullable) NSString *layerGroupKey;

#ifndef GLYPHS_VIEWER
/// Whether the layer is displayed with other layers in Edit view.
///
/// A master layer uses the visibility of its associated font master.
@property (nonatomic) BOOL visible;

/// A property-list representation for the specified Glyphs file format version.
///
/// - Parameter format: The Glyphs file format version for which to encode the layer.
- (nullable NSDictionary<NSString *, id> *)propertyListValueFormat:(GSFormatVersion)format error:(out NSError **)error;
#endif
/// The identifier under which the layer is stored in its glyph.
///
/// A master layer uses the identifier of its font master.
@property (strong, nonatomic) NSString *layerId;

/// The identifier of the font master that supplies metrics and other master-specific values.
///
/// For a master layer, this is the same as ``layerId``.
@property (strong, nonatomic, nullable) NSString *associatedMasterId;
#ifndef GLYPHS_VIEWER

/// Kerning dictionary for observing kerning changes.
///
/// This property is only a KVO surface.
/// Use the kerning accessors to read or change pair values.
@property (readonly, nonatomic, nullable) id kerning;

/// @name Metrics

/// Horizontal distance from the origin to the left outline edge.
@property (nonatomic) CGFloat LSB;

/// Cached left side bearing without updating metrics.
///
/// Use this only when the metrics are already known to be current.
@property (nonatomic) CGFloat LSBFast;

/// Left side bearing without normalizing the no-outline value.
- (CGFloat)LSBPlain;

/// Horizontal distance from the right outline edge to the advance width.
@property (nonatomic) CGFloat RSB;

/// Right side bearing without normalizing the no-outline value.
- (CGFloat)RSBPlain;

/// Cached right side bearing without updating metrics.
///
/// Use this only when the metrics are already known to be current.
@property (nonatomic) CGFloat RSBFast;

/// Metric value for a side at a measurement height.
///
/// For horizontal side bearings, the height is a y coordinate.
/// For vertical side bearings, the height is an x coordinate.
/// Returns `NSNotFound` when no value can be measured.
///
/// - Parameter side: The metric side to measure.
/// - Parameter height: The coordinate at which to measure the side bearing.
- (CGFloat)metricsValue:(GSMetricNumbers)side atHeight:(NSInteger)height;

/// Left side bearing measured at a y coordinate.
///
/// Returns `NSNotFound` when no outline intersection can be measured at the height.
- (CGFloat)lsbAtHeight:(NSInteger)height;
/// Sets the left side bearing measured at a y coordinate.
- (void)setLsb:(CGFloat)newValue atHeight:(NSInteger)atHeight NS_SWIFT_NAME(setLSB(_:atHeight:));
/// Right side bearing measured at a y coordinate.
///
/// Returns `NSNotFound` when no outline intersection can be measured at the height.
- (CGFloat)rsbAtHeight:(NSInteger)height;
/// Sets the right side bearing measured at a y coordinate.
- (void)setRsb:(CGFloat)newValue atHeight:(NSInteger)atHeight NS_SWIFT_NAME(setRSB(_:atHeight:));

/// Top side bearing measured at an x coordinate.
///
/// Returns `NSNotFound` when no outline intersection can be measured at the coordinate.
- (CGFloat)tsbAtHeight:(NSInteger)height;
/// Sets the top side bearing measured at an x coordinate.
- (void)setTsb:(CGFloat)newValue atHeight:(NSInteger)atHeight NS_SWIFT_NAME(setTSB(_:atHeight:));
/// Bottom side bearing measured at an x coordinate.
///
/// Returns `NSNotFound` when no outline intersection can be measured at the coordinate.
- (CGFloat)bsbAtHeight:(NSInteger)height;
/// Sets the bottom side bearing measured at an x coordinate.
- (void)setBsb:(CGFloat)newValue atHeight:(NSInteger)atHeight NS_SWIFT_NAME(setBSB(_:atHeight:));

#endif

/// Advance width for horizontal text layout.
@property (nonatomic, assign) CGFloat width;

/// Distance from the top metric to the top outline edge.
@property (nonatomic, assign) CGFloat TSB;

/// Cached top side bearing without updating metrics.
///
/// Use this only when the metrics are already known to be current.
@property (nonatomic, assign) CGFloat TSBFast;

/// Top side bearing without normalizing the no-outline value.
- (CGFloat)TSBPlain;

/// Distance from the bottom outline edge to the vertical advance.
@property (nonatomic, assign) CGFloat BSB;

/// Cached bottom side bearing without updating metrics.
///
/// Use this only when the metrics are already known to be current.
@property (nonatomic, assign) CGFloat BSBFast;

/// Bottom side bearing without normalizing the no-outline value.
- (CGFloat)BSBPlain;

/// Advance width for vertical text layout.
@property (nonatomic, assign) CGFloat vertWidth;

/// Vertical origin offset for vertical text layout.
@property (nonatomic) CGFloat vertOrigin;

#ifndef GLYPHS_VIEWER
/// Whether automatic alignment controls the layer width.
@property (readonly) BOOL hasAlignedWidth;

/// Whether components are currently aligned by automatic alignment.
@property (nonatomic, readonly) BOOL isAligned;

/// Whether automatic alignment controls side bearings.
- (BOOL)hasAlignedSideBearings;
#endif

/// Whether an automatic alignment operation is in progress.
@property BOOL isAligning;

#ifndef GLYPHS_VIEWER
/// Layer-specific metrics key for the left side bearing.
///
/// When `nil`, the glyph-level metrics key can still apply.
@property (nonatomic, strong, nullable) NSString *leftMetricsKey;

/// Layer-specific metrics key for the advance width.
///
/// When `nil`, the glyph-level metrics key can still apply.
@property (nonatomic, strong, nullable) NSString *widthMetricsKey;

/// Layer-specific metrics key for the right side bearing.
///
/// When `nil`, the glyph-level metrics key can still apply.
@property (nonatomic, strong, nullable) NSString *rightMetricsKey;

/// Layer-specific metrics key for the top side bearing.
///
/// When `nil`, the glyph-level metrics key can still apply.
@property (nonatomic, strong, nullable) NSString *topMetricsKey;

/// Layer-specific metrics key for the bottom side bearing.
///
/// When `nil`, the glyph-level metrics key can still apply.
@property (nonatomic, strong, nullable) NSString *bottomMetricsKey;

/// Layer-specific metrics key for the vertical advance width.
///
/// When `nil`, the glyph-level metrics key can still apply.
@property (nonatomic, strong, nullable) NSString *vertWidthMetricsKey;

/// Layer-specific metrics key for the vertical origin.
///
/// When `nil`, the glyph-level metrics key can still apply.
@property (nonatomic, strong, nullable) NSString *vertOriginMetricsKey;

/// Value described by a metrics key for a metric side.
///
/// Returns `NSNotFound` when the key cannot be resolved.
///
/// - Parameter metric: The metric side that the key is evaluated for.
/// - Parameter key: The metrics key to evaluate.
- (CGFloat)metricsValue:(GSMetricNumbers)metric forKey:(NSString *)key NS_REFINED_FOR_SWIFT;
/// Evaluates a metrics key for a metric side.
///
/// - Parameter metric: The metric side that the key is evaluated for.
/// - Parameter key: The metrics key to evaluate.
/// - Parameter value: On return, the resolved metric value.
/// - Parameter atHeight: On return, the height qualifier from the key, or `NSNotFound`.
/// - Returns: `YES` when the key syntax is valid.
- (BOOL)metricsValue:(GSMetricNumbers)metric forKey:(NSString *)key value:(CGFloat *)value atHeight:(NSInteger *)atHeight NS_REFINED_FOR_SWIFT;
/// Parses a metrics key into its referenced glyph, operator, numeric value, opposite side, height, and master.
///
/// This parses metrics key syntax without evaluating the referenced layer.
///
/// - Parameter glyphName: On return, the referenced glyph name.
/// - Parameter aOperator: On return, the arithmetic operator encoded in the key.
/// - Parameter value: On return, the numeric value encoded in the key.
/// - Parameter oppositeSite: On return, whether the key references the opposite side.
/// - Parameter atHeight: On return, the height qualifier from the key, or `NSNotFound`.
/// - Parameter masterName: On return, the referenced master name.
/// - Returns: `YES` when the key syntax is valid.
+ (BOOL)metricsKey:(nullable NSString *)key name:(NSString *_Nullable *_Nonnull)glyphName operator:(char *)aOperator value:(CGFloat *)value oppositeSite:(short *)oppositeSite atHeight:(NSInteger *)atHeight master:(NSString *_Nullable *_Nullable)masterName;
/// Moves the shapes, anchors, guides, and background image by an offset.
///
/// The layer width and hints are not changed.
///
/// - Parameter offset: The horizontal and vertical displacement in layer coordinates.
- (void)moveContent:(NSPoint)offset;
#endif

/// @name Bezier Handling

/// A cached representation of the layer’s closed path shapes.
///
/// Components and open paths are excluded.
/// Returns `nil` when the layer has no shapes.
@property (nonatomic, strong, nullable) NSBezierPath *bezierPath;

/// A cached representation of the closed path shapes without full preview processing.
///
/// Components and open paths are excluded.
/// Returns `nil` when the layer has no shapes.
- (nullable NSBezierPath *)bezierPathFast;

/// A cached representation of the layer’s open path shapes.
///
/// Components and closed paths are excluded.
/// Returns `nil` when the layer has no open paths.
@property (nonatomic, strong, nullable) NSBezierPath *openBezierPath;

/// A cached representation of the selected outline segments.
@property (nonatomic, strong, nullable) NSBezierPath *selectionPath;

/// A representation of the closed paths and component outlines.
- (nullable NSBezierPath *)drawBezierPath NS_RETURNS_RETAINED;

/// Creates a representation of the closed paths and component outlines while tracking component recursion.
///
/// - Parameter seenComponents: The glyph names already visited in the current component hierarchy.
/// - Parameter hasPaths: On return, the logical OR of its initial value and whether the layer or one of its components contains paths.
- (nullable NSBezierPath *)drawBezierPath:(NSMutableSet *)seenComponents hasPaths:(BOOL *)hasPaths NS_RETURNS_RETAINED;

/// A representation of the open paths in the layer and its components.
- (nullable NSBezierPath *)drawOpenBezierPath;

/// Returns the cached representation of open paths without updating it.
///
/// Access ``drawBezierPath`` immediately before using this method.
- (nullable NSBezierPath *)drawOpenBezierPathFast;

/// Auxiliary outlines generated by the most recent full Bézier path preparation.
@property (nonatomic, strong, nullable) NSBezierPath *secondaryPath;

/// Interactive handles generated by the most recent full Bézier path preparation.
@property (nonatomic, strong, nullable) NSMutableArray *extraHandles;

#ifndef GLYPHS_LITE
/// Applies TrueType hints to the layer at a scale.
///
/// This mutates outline coordinates.
/// - Parameter scale: The scale used to round hinted coordinates.
- (void)applyHints:(CGFloat)scale;
#endif

/// The shape attributes applied to newly drawn shapes.
@property (nonatomic, strong, nullable) NSDictionary<GSShapeKey, id> *currentDrawingAttributes;

/// Sets or removes an attribute applied to newly drawn shapes.
///
/// - Parameter attr: The attribute value, or `nil` to remove the attribute.
/// - Parameter key: The shape attribute key.
- (void)setCurrentDrawingAttribute:(nullable id)attr forKey:(NSString *)key;

/// Adds shape attributes applied to newly drawn shapes.
- (void)addCurrentDrawingAttributes:(NSDictionary<GSShapeKey, id> *)attrs;

/// Prepares path component hints for direct pen drawing.
///
/// Balance this call with ``finishDrawing`` after drawing the layer’s shapes directly.
- (void)prepareDrawing;

/// Finishes direct pen drawing prepared by ``prepareDrawing``.
- (void)finishDrawing;

#ifndef GLYPHS_LITE
/// The painted drawing operations for the complete layer.
///
/// The stack preserves masks, strokes, and composition groups.
- (NSArray<GSDrawSlice<NSBezierPath *> *> *)drawStack;

/// Creates painted drawing operations for a shape group.
///
/// Unsupported features are converted to simpler drawing operations where possible.
///
/// - Parameter group: The shape group to process.
/// - Parameter supportedFeatures: The draw-stack features the consumer can process.
/// - Parameter penClass: A pen class used to collect path drawing operations.
/// - Parameter secondPen: A pen that receives auxiliary outlines.
/// - Parameter extraHandles: A collection that receives generated interactive handles.
- (NSArray<GSDrawSlice *> *)drawStackForGroup:(GSShapeGroup *)group
								   supporting:(GSDrawStackFeature)supportedFeatures
									 penClass:(Class)penClass
								 secondaryPen:(nullable NSObject<GSPenProtocol> *)secondPen
								 extraHandles:(nullable NSMutableArray *)extraHandles;
#endif

/// The selected node when exactly one node is selected.
///
/// Returns `nil` when the selection is empty, contains multiple objects, or contains a non-node object.
@property (nonatomic, unsafe_unretained, readonly, nullable) GSNode *selNode NS_SWIFT_NAME(selectedNode);

/// The annotations stored on the layer.
///
/// Use the annotation mutation methods to preserve ownership, undo, layer change notifications, and selection state.
@property (nonatomic, strong) NSArray<GSAnnotation *> *annotations;

/// Creates a layer from a property-list representation.
///
/// - Parameter layerDict: The encoded layer values.
/// - Parameter formatVersion: The Glyphs file format version of the representation.
- (instancetype)initWithDict:(NSDictionary<NSString *, id> *)layerDict format:(GSFormatVersion)formatVersion;

#ifndef GLYPHS_VIEWER
/// Writes the layer in the specified Glyphs file format.
///
/// - Parameter file: The open file to which to write.
/// - Parameter formatVersion: The Glyphs file format version to use.
/// - Parameter context: The context shared by the enclosing save operation.
/// - Parameter error: On failure, information about the error.
/// - Returns: Whether the layer was written.
- (BOOL)saveToFile:(FILE *)file format:(GSFormatVersion)formatVersion context:(GSSaveContext *)context error:(NSError **)error;

/// An attribute representation for the specified Glyphs file format version.
///
/// The representation converts axis identifiers and fills values required by older formats.
///
/// - Parameter format: The Glyphs file format version for which to encode the attributes.
- (NSDictionary<GSLayerAttribute, id> *)attributesDict:(GSFormatVersion)format;
#endif

#ifndef GLYPHS_LITE
/// A signature describing the interpolation-relevant structure of the layer.
///
/// Equal signatures indicate compatible shape structures, anchor names, and path-component hints.
/// Positions and other geometric values are not represented.
- (NSString *)compareString;

/// A signature describing selected parts of the interpolation-relevant structure.
///
/// - Parameter options: The structures to include in the signature.
- (NSString *)compareStringOptions:(GSLayerCompareFlags)options;

/// The individual parts that form a structural compatibility signature.
///
/// - Parameter options: The structures to include.
- (NSArray *)compareParticlesOptions:(GSLayerCompareFlags)options;
#endif

/// Whether another layer has equal metrics, attributes, shapes, anchors, hints, guides, and background content.
///
/// Layer identity, ownership, names, and color labels are not compared.
///
/// - Parameter other: The layer to compare.
- (BOOL)isEqualToLayer:(GSLayer *)other;

/// The most recent update affecting the layer or its referenced content, while avoiding component cycles.
///
/// - Parameter seenComponents: Glyph names already visited while traversing components.
- (NSTimeInterval)lastOperation:(NSMutableSet *)seenComponents;

/// The most recent update affecting the layer or its referenced content.
///
/// Referenced content includes component and corner glyphs.
@property (nonatomic, readonly) NSTimeInterval lastOperation;

/// Whether derived representations are newer than changes that affect the layer.
///
/// Relevant changes include changes to the glyph, referenced content, and kerning.
@property (nonatomic, readonly, getter=isUpToDate) BOOL upToDate;

/// Whether clients should prevent editing the layer.
@property BOOL locked;

/// Whether the layer is the master layer for its associated font master.
///
/// Master layers provide the default outline and metrics for a font master.
@property (nonatomic, readonly, getter=isMasterLayer) BOOL masterLayer;

#ifndef GLYPHS_LITE
/// Whether the layer is a master layer, has special layer attributes, or uses any color-layer format.
@property (nonatomic, readonly, getter=isAnySpecialLayer) BOOL anySpecialLayer;
#endif

/// Whether the layer uses any color-layer format.
///
/// Color-layer formats can overlap with other layer kinds, such as Intermediate layers.
/// The specific color-layer format properties are not mutually exclusive.
@property (nonatomic, readonly, getter=isAnyColorLayer) BOOL anyColorLayer;

/// Whether the layer is a full-color layer.
///
/// Setting this property marks or unmarks the layer as a full-color layer.
@property (nonatomic, getter=isFullColorLayer) BOOL fullColorLayer;

/// Whether the first shape is a component whose glyph uses a color-layer format.
@property (nonatomic, readonly) BOOL hasColorsInComponents;

/// Whether the layer has special layer attributes.
///
/// Any layer with attributes is a special layer, including Intermediate and Alternative layer that use attributes to store their specific info.
@property (nonatomic, readonly, getter=isSpecialLayer) BOOL specialLayer;

#ifndef GLYPHS_LITE
/// Whether the layer is an intermediate layer at explicit design-space coordinates.
///
/// Setting this property marks or unmarks the layer as an Intermediate layer.
/// Use ``coordinates`` to read or set the coordinates.
@property (nonatomic, getter=isBraceLayer) BOOL braceLayer;

/// Whether the layer is an alternative layer selected by axis rules.
///
/// Setting this property marks or unmarks the layer as an Alternative layer.
/// Use ``axisRules`` to read or set the rules.
@property (nonatomic, getter=isBracketLayer) BOOL bracketLayer;

/// Whether the layer is an Apple bitmap color layer.
///
/// Setting this property marks or unmarks the layer as an Apple bitmap color layer.
@property (nonatomic, getter=isAppleColorLayer) BOOL appleColorLayer;

/// Whether the layer is an SVG color layer.
///
/// Setting this property marks or unmarks the layer as an SVG color layer.
@property (nonatomic, getter=isSVGColorLayer) BOOL SVGColorLayer;

/// Whether the layer is a palette color layer.
///
/// Setting this property marks or unmarks the layer as a palette color layer.
@property (nonatomic, getter=isColorPaletteLayer) BOOL colorPaletteLayer;

/// Whether the layer is an intermediate layer for a smart glyph.
///
/// Smart-component layers are Intermediate layers that use glyph-local axes.
@property (nonatomic, getter=isSmartComponentLayer, readonly) BOOL smartComponentLayer;

/// Copies explicit Intermediate-layer coordinates into an axis-value buffer.
///
/// Values for axes without an explicit coordinate are left unchanged.
///
/// - Parameter axesValuesList: A buffer with at least one entry for each axis.
/// - Parameter axes: The axes that determine the buffer order.
/// - Returns: Whether the layer has a valid coordinates dictionary.
- (BOOL)braceAxesValuesList:(CGFloat *)axesValuesList fontAxes:(NSArray<GSAxis *> *)axes;

/// Internal values for the requested axes.
///
/// Explicit Intermediate-layer coordinates take precedence over associated master values and axis defaults.
///
/// - Parameter axes: The axes that determine the value order.
- (NSArray<NSNumber *> *)axesValuesArrayFontAxes:(NSArray<GSAxis *> *)axes;

/// Copies the internal values for the requested axes into a buffer.
///
/// Explicit Intermediate-layer coordinates take precedence over associated master values and axis defaults.
///
/// - Parameter axesValuesList: A buffer with at least one entry for each axis.
/// - Parameter axes: The axes that determine the buffer order.
- (void)axesValuesList:(CGFloat *)axesValuesList fontAxes:(NSArray<GSAxis *> *)axes;

/// Internal value for an axis.
///
/// An explicit Intermediate-layer coordinate takes precedence over the associated master value and the axis default.
/// Returns `NSNotFound` when the axis is unknown.
///
/// - Parameter axisId: The axis identifier.
- (CGFloat)axisInternalValueValueForId:(NSString *)axisId NS_REFINED_FOR_SWIFT;

#endif

#ifndef GLYPHS_VIEWER
/// Returns the nearest node to a point, excluding one node.
///
/// Locked paths and nodes are skipped when `ignoreLocked` is `YES`.
/// The search is limited to nodes on this layer.
/// - Returns: The nearest node within `tolerance`, or `nil` if no node matches.
- (nullable GSNode *)nodeAtPoint:(NSPoint)aPoint excludeNode:(nullable GSNode *)exclude ignoreLocked:(BOOL)ignoreLocked tolerance:(CGFloat)tolerance;

/// Returns the nearest node to a point, excluding a set of objects.
///
/// Locked paths and nodes are skipped when `ignoreLocked` is `YES`.
///
/// - Parameter traverseComponents: When `YES`, the search uses node positions from component outlines and returns a transient node.
/// - Parameter excludes: Nodes or components to exclude from the search.
/// - Returns: The nearest node within `tolerance`, or `nil` if no node matches.
- (nullable GSNode *)nodeAtPoint:(NSPoint)aPoint excludeNodes:(nullable NSSet<GSNode *> *)excludes traverseComponents:(BOOL)traverseComponents ignoreLocked:(BOOL)ignoreLocked tolerance:(CGFloat)tolerance;
#endif

#pragma mark Shapes

/// @name Shapes

/// An array of the shape objects defining the visual part of the layer.
///
/// This is a linear representation of the shapes of the layer with the elements in stacking order from bottom to top.
/// This array also contains shape group objects which are used to create a shape tree structure.
/// When processing the shapes of a layer while respecting this shape hierarchy, use the `rootGroup` property instead.
@property (strong, nonatomic) NSArray<GSShape *> *shapes;

/// Replaces the shapes without undo registration or KVO notifications.
///
/// This is intended for controlled setup and batch operations where the caller handles external updates.
/// Shape ownership and shape groups are updated.
- (void)setShapesFast:(NSArray<GSShape *> *)shapes;

/// Replaces the shapes and hints together.
///
/// Use this when the replacement content already has matching hint data.
- (void)setShapes:(NSArray<GSShape *> *)shapes hints:(NSArray<GSHint *> *)hints;

/// Copies shapes, hints, anchors, and optionally selected nodes from another layer.
///
/// - Parameter doSelection: Whether selected nodes should be mapped from the source layer to the copied shapes.
- (void)getCopyOfContentFromLayer:(GSLayer *)shadowLayer doSelection:(BOOL)doSelection;

/// The number of shapes in the layer.
- (NSUInteger)countOfShapes;

/// Inserts a shape at the given linear shape index.
- (void)insertObject:(GSShape *)shape inShapesAtIndex:(NSInteger)idx NS_SWIFT_NAME(insertShape(_:atIndex:));

/// Adds a shape at the top of the layer.
///
/// The shape receives this layer as its parent.
- (void)addShape:(GSShape *)shape;

/// Adds a shape at the top of the layer without undo registration or KVO notifications.
///
/// The shape receives this layer as its parent.
- (void)addShapeFast:(GSShape *)shape;

/// Adds shapes at the top of the layer.
///
/// The shapes receive this layer as their parent.
- (void)addShapes:(NSArray<GSShape *> *)shapes;

/// Adds shapes at the top of the layer without undo registration or KVO notifications.
///
/// The shapes receive this layer as their parent.
- (void)addShapesFast:(NSArray<GSShape *> *)shapes;

/// A proxy for iterating and accessing path shapes.
///
/// The proxy filters the layer’s shapes to paths.
@property (nonatomic, readonly) GSProxyShapes *paths;
/// A proxy for iterating and accessing component shapes.
///
/// The proxy filters the layer’s shapes to components.
@property (nonatomic, readonly) GSProxyShapes *components;

/// The component shapes in the layer.
///
/// Returns `nil` when the layer has no component shapes.
@property (nonatomic, nullable, readonly) NSArray<GSComponent *> *allComponents;

/// Invokes the block for all shapes in linear shape order.
///
/// Shape groups are included in the enumeration.
- (void)enumerateShapesUsingBlock:(void (^)(GSShape *obj, NSInteger shapeIdx, BOOL *stop))block;

/// Invokes the block for all path shapes in linear shape order.
- (void)enumeratePathsUsingBlock:(void (^)(GSPath *path, NSInteger shapeIdx, BOOL *stop))block;

/// Invokes the block for all path shapes using the given enumeration options.
- (void)enumeratePathsWithOptions:(NSEnumerationOptions)opts usingBlock:(void (^)(GSPath *path, NSInteger shapeIdx, BOOL *stop))block;

/// Invokes the block for all component shapes in linear shape order.
- (void)enumerateComponentsUsingBlock:(void (^)(GSComponent *component, NSInteger shapeIdx, BOOL *stop))block;

/// Invokes the block for all component shapes using the given enumeration options.
- (void)enumerateComponentsWithOptions:(NSEnumerationOptions)opts usingBlock:(void (^)(GSComponent *component, NSInteger shapeIdx, BOOL *stop))block;

/// Removes a shape from the layer.
///
/// The shape is also removed from the selection and no longer has this layer as its parent.
- (void)removeShape:(GSShape *)shape;

/// Removes shapes from the layer.
- (void)removeShapes:(NSArray<GSShape *> *)shapes;

/// Removes the shape at the given linear shape index.
- (void)removeObjectFromShapesAtIndex:(NSUInteger)idx NS_SWIFT_NAME(removeShape(atIndex:));

/// Removes the shapes at the given linear shape indexes.
- (void)removeShapesAtIndexes:(NSIndexSet *)indexes;

/// Replaces the shape at the given linear shape index.
///
/// The new shape receives this layer as its parent and the old shape is detached.
- (void)replaceShapeAtIndex:(NSUInteger)idx withShape:(GSShape *)shape;

/// The shape groups keyed by group identifier.
///
/// The root group is keyed by the root group identifier.
@property (readonly) NSDictionary<NSString *, GSShapeGroup *> *shapeGroups;

/// A shape group containing all shapes of the layer.
///
/// This is the root of the shape tree structure of the layer.
/// The shapes of this layer are the tree nodes in this hierarchical representation.
/// Other shape groups create nested subtrees.
///
/// When processing the shapes of a layer while respecting this shape hierarchy, use this property and a tree traversal algorithm such as the enumeration methods on `GSShapeGroup`.
/// Otherwise, use the `shapes` property for a linear representation of the shapes of the layer.
@property (readonly) GSShapeGroup *rootGroup;

/// Passes each color used directly by the shapes of the layer to a block and updates it when the block returns a new color.
///
/// Fill and stroke colors, gradient colors, and shadow colors are included.
/// Colors in component glyphs are not included.
///
/// Return the original color or `nil` to leave a color unchanged.
- (void)transformColorsUsingBlock:(NSColor * _Nullable (^)(NSColor *))block;

/// Copies shape positions and related hint positions from another layer.
///
/// Shapes and hints are matched by index.
- (void)getPositionsFromLayer:(GSLayer *)otherLayer;

/// Connects two path endpoints.
///
/// Endpoints on different open paths join the paths.
/// Endpoints on the same open path close it.
///
/// - Returns: The surviving node at the connection, or `nil` when the nodes cannot be connected.
- (nullable GSNode *)connectPathsWithNode:(GSNode *)moveNode andNode:(GSNode *)overNode;

/// Connects two path endpoints and optionally adjusts their adjacent segments.
///
/// Endpoints on different open paths join the paths.
/// Endpoints on the same open path close it.
///
/// - Parameter extendPath: Whether to extend adjacent segments to a suitable intersection before connecting them.
/// - Returns: The surviving node at the connection, or `nil` when the nodes cannot be connected.
- (nullable GSNode *)connectPathsWithNode:(GSNode *)moveNode andNode:(GSNode *)overNode extendPath:(BOOL)extendPath;

/// Opens or divides the path at a node.
///
/// A closed path is opened at the node.
/// An open path is divided into two paths with a copy of the node at the new opening.
///
/// - Returns: The node preceding the new opening, or `nil` when the node cannot be processed.
- (nullable GSNode *)dividePathAtNode:(GSNode *)node;

/// Divides paths at two on-curve nodes and reconnects the resulting endpoints crosswise.
///
/// - Parameter offset: The distance by which to extend adjacent segments before reconnecting them.
- (void)reconnectNodesAtNode1:(GSNode *)node1 node2:(GSNode *)node2 offset:(CGFloat)offset;

/// Reconnects four on-curve nodes according to their tangent directions.
///
/// - Returns: Whether all resulting endpoints could be paired and connected.
- (BOOL)reconnectNodes:(NSArray<GSNode *> *)originNodes;

/// Connects coincident endpoints of open paths.
///
/// When the layer has a selection, only connections between a selected and an unselected endpoint are made.
/// Paths must belong to the same shape group.
- (void)connectAllOpenPaths;

/// Connects the nearest endpoints of a collection of open paths.
- (void)closeOpenPaths:(NSArray<GSPath *> *)paths;

/// Notifies the layer that one of its elements changed.
///
/// Pass `nil` when the changed element is not known.
///
/// - Parameter element: The changed element.
- (void)elementDidChange:(nullable id)element;

/// Reconfigures the shape groups (setting their member ranges) and reorders `shapes` to match.
///
/// - Parameter skipPreconditionCheck: If the caller knows that the layer makes use of shape groups, performance may be improved by skipping an internal precondition check. The function produces the same result in either case; only the performance is impacted.
///
/// - Complexity: O(*n*) where *n* is the length of `_shapes`.
- (void)reconfigureShapeGroupsSkippingPreconditionCheck:(BOOL)skipPreconditionCheck;

/// Returns `subjects` reordered such that the relative order is the same as in `shapes`, excluding any shapes in `subjects` that are members of a shape group in `subjects`.
///
/// Returns `nil` if the given array is empty or any of the shapes is not part of the layer.
- (nullable NSArray<GSShape *> *)handleShapesFromShapes:(NSArray<GSShape *> *)subjects;

/// Moves or copies the given shapes to a new location in the layer’s shape group tree.
///
/// - Parameter shapes: The shapes to move or copy.
/// - Parameter copyBuffer: A buffer for the copied shapes. If `nil`, the shapes are moved. If not `nil`, the shapes are copied and the buffer is populated with the copied shapes, sharing the same indices as the source `shapes`.
/// - Parameter parentGroup: The new parent group for the given shapes. If `nil`, the shapes are moved to the root of the layer’s shape group tree.
/// - Parameter childIndex: The insertion index for the given shapes in the new parent group. Note that this index must be valid after the given shapes have temporarily been removed from the layer’s shape group tree.
- (BOOL)moveShapes:(NSArray<GSShape *> *)shapes copyBuffer:(nullable NSMutableArray<GSShape *> *)copyBuffer intoGroup:(nullable GSShapeGroup *)parentGroup childIndex:(NSUInteger)childIndex;

/// Combines the given shapes into a new shape group.
///
/// The newly created shape group is placed in the layer’s shape group tree where the topmost shape in `memberCandidates` is located.
///
/// Any shape groups in `memberCandidates` are moved together with all their members into the new shape group.
/// Therefore, for any shape group in `memberCandidates`, it is not necessary to include any of its members in `memberCandidates`, but their inclusion is allowed.
///
/// The new shape group contains the given shapes in the same relative order as they appear in the layer’s `shapes` array.
///
/// - Returns: The newly created shape group. `nil` is returned if one of the given shapes is not part of the layer’s shapes or the given shapes array is empty.
- (nullable GSShapeGroup *)createShapeGroupFromShapes:(NSArray<GSShape *> *)memberCandidates;

/// Dissolves the given shape group by replacing it with its direct members.
///
/// - Returns: `NO` if the given shape group is not part of the layer.
- (BOOL)dissolveShapeGroup:(GSShapeGroup *)group;

/// Invokes a block for the layers in the same layer group that participate in synced editing.
///
/// The receiver is always included.
/// Other layer group members are included when synced editing is enabled.
///
/// Synced editing is controlled by the `GSSyncLayerEdits` user defaults value.
- (void)enumerateSyncedLayerGroupMembersUsingBlock:(void (^)(GSLayer *))block;

/// Invokes a block for the layers of the glyph that participate in synced editing.
///
/// The receiver is always included.
/// Other non-backup layers are included when synced editing is enabled.
///
/// Synced editing is controlled by the `GSSyncLayerEdits` user defaults value.
- (void)enumerateSyncedLayersUsingBlock:(void (^)(GSLayer *))block;

/// Invokes a block for the other layers in the same layer group that participate in synced editing.
///
/// The receiver is excluded.
/// The block is not invoked when synced editing is disabled.
- (void)enumerateSyncedLayerGroupAssociatesUsingBlock:(void (^)(GSLayer *))block;

/// The number of path shapes in the layer.
@property (nonatomic, readonly) NSUInteger countOfPaths;

/// The number of component shapes in the layer.
@property (nonatomic, readonly) NSUInteger countOfComponents;

/// The number of image shapes in the layer.
@property (nonatomic, readonly) NSUInteger countOfImages;

/// Returns the linear shape index of a shape.
- (NSUInteger)indexOfObjectInShapes:(GSShape *)shape NS_REFINED_FOR_SWIFT;

/// Returns the linear shape index of a path.
///
/// This is a compatibility alias for ``indexOfObjectInShapes:``.
- (NSUInteger)indexOfPath:(GSShape *)shape NS_REFINED_FOR_SWIFT;

/// Returns the shape at the given linear shape index.
///
/// Returns `nil` when the index is out of range.
- (nullable GSShape *)objectInShapesAtIndex:(NSUInteger)idx NS_SWIFT_NAME(shape(atIndex:));

/// Returns the shape at the given linear shape index without locking.
///
/// Returns `nil` when the index is out of range.
- (nullable GSShape *)objectInShapesAtIndexFast:(NSUInteger)idx NS_SWIFT_NAME(shape(atIndexFast:));

/// Returns the node at an index path.
///
/// The first index is the linear path index and the second index is the node index in that path.
/// Returns `nil` if the first index does not refer to a path.
- (nullable GSNode *)nodeAtIndexPath:(NSIndexPath *)indexPath;

/// Returns the index path of a node in this layer.
///
/// The first index is the linear path index and the second index is the node index in that path.
/// Returns `nil` when the node does not belong to this layer.
- (nullable NSIndexPath *)indexPathOfNode:(GSNode *)node;

/// All nodes from all path shapes in linear shape order.
///
/// Returns `nil` when the layer has no nodes.
@property (nonatomic, nullable, readonly) NSArray<GSNode *> *allNodes;

/// The number of nodes in all path shapes.
- (NSUInteger)allNodesCount;

/// The path segments from all path shapes in linear shape order.
- (NSArray<GSPathSegment *> *)segments;

/// Returns the segment at an index path.
///
/// The first index is the linear path index and the last index is the segment node index in that path.
/// Returns `nil` if the first index does not refer to a path.
- (nullable GSPathSegment *)segmentAtIndexPath:(NSIndexPath *)indexPath;

/// The selected nodes in path order.
///
/// For partial closed-path selections, wrapped selections are ordered around the path start.
- (NSArray<GSNode *> *)sortedSelectedNodes;

#pragma mark Anchors

/// @name Anchors

/// Layer anchors keyed by anchor name.
///
/// The dictionary keys are the anchor names.
/// Setting this property replaces all anchors.
@property (strong, nonatomic) NSDictionary<NSString *, GSAnchor *> *anchors;

/// The number of anchors on the layer.
- (NSInteger)countOfAnchors;

#ifndef GLYPHS_VIEWER

/// Internal fast anchor setter.
- (void)setAnchorsFast:(MGOrderedDictionary *)anchors;

/// Adds or replaces an anchor by name.
///
/// An unnamed anchor receives a generated name.
- (void)addAnchor:(GSAnchor *)anchor;

/// Add the anchors form the list
- (void)addAnchors:(NSArray <GSAnchor *> *)anchors;

/// Add the anchors form the list without undo and KVO
- (void)addAnchorsFast:(NSArray <GSAnchor *> *)anchors;

/// Replaces all anchors with anchors from an array.
- (void)setAnchorArray:(NSArray<GSAnchor *> *)anchors;

/// Replaces all anchors without undo registration or change notifications.
- (void)setAnchorArrayFast:(NSArray<GSAnchor *> *)anchors;

/// Removes an anchor from the layer.
///
/// The anchor must belong to the layer.
- (void)removeAnchor:(GSAnchor *)anchor;

/// Removes the anchor with a name.
- (void)removeAnchorWithName:(NSString *)anchorName;
#endif

/// Swaps two anchor names without undo registration or change notifications.
///
/// If only one anchor exists, that anchor is renamed.
- (void)switchAnchorFastName:(NSString *)name1 withName:(NSString *)name2;

/// Anchor for a name.
- (nullable GSAnchor *)anchorForName:(NSString *)name;

/// Anchor for a name, optionally resolving component anchors.
///
/// Component traversal applies component transforms and component anchor settings.
/// Returns `nil` when no anchor is available for the name.
///
/// - Parameter checkComponents: Whether to include anchors resolved from components.
- (nullable GSAnchor *)anchorForName:(NSString *)name traverseComponents:(BOOL)checkComponents;

/// Anchor names available after resolving component anchors.
- (NSArray<NSString *> *)anchorNamesTraversingComponents;

/// Anchor names available before a component in shape order.
///
/// Returns `nil` for component cycles.
- (nullable NSArray<NSString *> *)anchorNamesTraversingComponentsStopAtComponent:(GSComponent *)stopComponent;

/// Anchors available after resolving component anchors.
- (NSArray<GSAnchor *> *)anchorsTraversingComponents;

/// Anchors available after resolving component anchors, keyed by anchor name.
- (NSDictionary <NSString *, GSAnchor *>*)anchorDictTraversingComponents;

/// Anchor name adjusted for a component scale.
///
/// Mirrored scales swap directional anchor name parts such as `left` and `right`.
+ (NSString *)anchorName:(NSString *)currAnchorKey forScale:(NSPoint)scale;

/// Updates layer anchor lookup after an owned anchor changes its name.
- (void)anchorChangedName:(GSAnchor *)anchor;

/// Copies resolved component anchors into the layer.
- (void)copyAnchorsFromComponents;

/// Decomposes corner components.
- (void)decomposeCorners;

/// Decomposes smart outline components.
- (void)decomposeSmartOutlines;

/// Replaces the layer contents with their flattened path outlines and removes overlaps.
///
/// - Returns: Whether the outlines were flattened successfully.
- (BOOL)flattenOutlines;

/// Replaces the layer contents with their flattened path outlines.
///
/// Components, stroke paint, masks, and other shape composition are resolved into paths.
///
/// - Parameter removeOverlap: Whether to remove overlaps from outlines of moderate complexity.
/// - Parameter origHints: Original hints that receive updated corner representation data.
/// - Parameter secondaryPath: A path to which the original path outlines are appended.
/// - Parameter extraHandles: A collection to which handles produced while flattening are appended.
/// - Returns: Whether the outlines were flattened successfully.
- (BOOL)flattenOutlinesRemoveOverlap:(BOOL)removeOverlap origHints:(nullable NSArray<GSHint *> *)origHints secondaryPath:(nullable NSBezierPath *)secondaryPath extraHandles:(nullable NSMutableArray *)extraHandles error:(NSError **)error;

/// Replaces the layer contents with flattened path outlines for a drawing feature set.
///
/// Components, stroke paint, masks, and other unsupported shape composition are resolved into paths.
///
/// - Parameter removeOverlap: Whether to remove overlaps from outlines of moderate complexity.
/// - Parameter origHints: Original hints that receive updated corner representation data.
/// - Parameter supportedFeatures: Shape composition features that may remain in the flattened result.
/// - Parameter secondaryPath: A path to which the original path outlines are appended.
/// - Parameter extraHandles: A collection to which handles produced while flattening are appended.
/// - Returns: Whether the outlines were flattened successfully.
- (BOOL)flattenOutlinesRemoveOverlap:(BOOL)removeOverlap origHints:(nullable NSArray<GSHint *> *)origHints supporting:(GSDrawStackFeature)supportedFeatures secondaryPath:(nullable NSBezierPath *)secondaryPath extraHandles:(nullable NSMutableArray *)extraHandles error:(NSError **)error;

/// Builds component shapes from glyph information.
///
/// Existing shapes are replaced when a complete component setup can be found.
- (void)makeComponents;

/// Finds a component glyph name for a base glyph and suffix context.
///
/// - Returns: A glyph name that exists in the font, or `nil` if no matching component is found.
- (nullable NSString *)findComponent:(NSString *)accentName baseName:(NSString *)baseName withSuffix:(nullable NSString *)suffix;

/// A copy of the layer with components decomposed.
///
/// The returned layer contains outlines for the decomposed component content.
- (GSLayer *)copyDecomposedLayer;

/// Decomposes all components.
///
/// Nested components are decomposed as part of the operation.
- (void)decomposeComponents;

/// Decomposes all components and invokes a callback for decomposed component layers.
///
/// - Returns: `YES` if decomposition finished.
- (BOOL)decomposeComponentsCallback:(nullable BOOL (^)(GSComponent *component, GSLayer *__nonnull layer))layerCallback;

/// Decomposes one component.
- (void)decomposeComponent:(GSComponent *)component;

/// Decomposes one component without triggering layer updates.
///
/// Use this only inside code that handles the required updates itself.
- (void)decomposeComponentDontNotify:(GSComponent *)aComponent;

/// Decomposes one component without triggering layer updates and invokes a callback for decomposed component layers.
///
/// Use this only inside code that handles the required updates itself.
/// - Returns: `YES` if decomposition finished.
- (BOOL)decomposeComponentDontNotify:(GSComponent *)aComponent callback:(nullable BOOL (^)(GSComponent *component, GSLayer *__nonnull layer))layerCallback;

/// The component names of the direct component shapes.
///
/// Setting this property replaces the layer’s shapes with components for the given names.
@property (strong, nonatomic) NSArray<NSString *> *componentNames;

/// The component names of direct and nested component shapes.
@property (strong, nonatomic, readonly) NSArray<NSString *> *componentNamesTraversingComponents;

/// Returns component names, optionally including nested component shapes.
- (NSArray<NSString *> *)componentNamesTraverseComponents:(BOOL)traverseComponents;
#pragma mark Background

/// @name Background

#ifndef GLYPHS_VIEWER
/// Editable background content associated with the layer.
///
/// Reading this property creates an empty background layer when needed.
/// Use ``hasBackground`` to check for an existing background without creating one.
/// Set this property to `nil` to remove the stored background.
@property (nonatomic, strong, nullable) GSLayer *background;

/// Whether a background layer has been created.
///
/// This does not indicate whether the background contains any content.
- (BOOL)hasBackground;
#endif

/// Copies shapes and anchors to the corresponding foreground or background layer.
///
/// Calling this on a foreground layer copies to its background.
/// Calling it on a background layer copies to its foreground.
///
/// - Parameter checkSelection: Whether to copy only selected shapes and anchors.
/// - Parameter keep: Whether to preserve shapes and anchors already in the destination layer.
- (void)contentToBackgroundCheckSelection:(BOOL)checkSelection keepOldBackground:(BOOL)keep;

/// Exchanges shapes, anchors, and hints between the foreground and background layers.
///
/// Other layer content, including background images, is not exchanged.
- (void)swapForegroundWithBackground;

/// Removes shapes, anchors, hints, and selection from the layer.
- (void)clear;

#pragma mark BackgroundImage

/// @name Image

/// A reference image attached directly to the layer.
///
/// This image is independent of the layer’s editable background content.
@property (strong, nonatomic, nullable) GSBackgroundImage *backgroundImage;

/// Whether a background image is attached directly to the layer.
@property (nonatomic, readonly) BOOL hasBackgroundImage;

/// Adds an image from a file URL.
///
/// The image becomes the layer’s background image.
/// On a full-color layer, it is added as a shape instead.
///
/// - Parameter URL: The image file URL.
- (void)addBackgroundImageWithURL:(NSURL *)URL;

#pragma mark Hints

/// @name Hints

/// Layer hints.
///
/// The returned array is empty when no hints are stored.
@property (nonatomic, strong) NSArray<GSHint *> *hints;

/// Low-level hint storage access.
///
/// This may be `nil` and does not register undo or layer updates.
/// Use it only while setting up a layer or in code that handles the required updates itself.
@property (nonatomic, strong, nullable) NSMutableArray<GSHint *> *hintsFast;

/// The number of stored hints.
- (NSUInteger)countOfHints;

/// The index of a hint in the layer.
///
/// - Returns: The hint index, or `NSNotFound` if the hint is not in the layer.
- (NSUInteger)indexOfObjectInHints:(nonnull GSHint *)hint NS_REFINED_FOR_SWIFT;

/// The hint for a stable hint key.
///
/// - Returns: The matching hint, or `nil` if no hint has the key.
- (nullable GSHint *)hintWithKey:(NSString *)hintKey;

/// Whether the layer has PostScript or TrueType hints.
@property (nonatomic, readonly) BOOL hasHints;

/// Whether the layer has TrueType hints.
@property (nonatomic, readonly) BOOL hasTrueTypeHints;

/// Whether the layer has PostScript hints.
@property (nonatomic, readonly) BOOL hasPostScriptHints;

/// Whether the layer has path component hints.
@property (nonatomic, readonly) BOOL hasCorners;

/// The TrueType hints stored on the layer.
@property (nonatomic, readonly) NSArray<GSHint *> *allTrueTypeHints;

/// The PostScript hints stored on the layer.
@property (nonatomic, readonly) NSArray<GSHint *> *allPostScriptHints;

/// The path component hints stored on the layer.
@property (nonatomic, readonly) NSArray<GSHint *> *allPathComponents;

/// The number of PostScript hints.
///
/// If the layer has no own hints, component layers are included.
- (NSUInteger)countOfPostScriptHints;

/// Adds a hint.
- (void)addHint:(GSHint *)hint;

/// Adds a hint, checking for an existing path component hint at the same index path.
- (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 ;

/// The hint at an index.
///
/// - Returns: The hint, or `nil` if the index is out of bounds.
- (nullable GSHint *)objectInHintsAtIndex:(NSUInteger)idx NS_SWIFT_NAME(hint(atIndex:));

/// Inserts a hint at an index.
- (void)insertObject:(GSHint *)hint inHintsAtIndex:(NSUInteger)idx NS_SWIFT_NAME(insertHint(_:atIndex:));

/// Removes a hint.
- (void)removeHint:(GSHint *)hint;

/// Removes the hint at an index.
- (void)removeObjectFromHintsAtIndex:(NSUInteger)idx NS_SWIFT_NAME(removeHint(atIndex:));

/// Updates path component hints after referenced glyphs or components changed.
- (void)updateCorners;

#pragma mark Annotations

/// @name Annotations

/// Adds an annotation.
- (void)addAnnotation:(GSAnnotation *)anAnnotation;

/// Removes an annotation.
///
/// This has no effect when the annotation is not in the layer.
- (void)removeAnnotation:(GSAnnotation *)anAnnotation;

/// The number of annotations.
- (NSUInteger)countOfAnnotations;

#ifndef GLYPHS_VIEWER

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

/// The annotation at an index.
///
/// - Returns: The annotation, or `nil` if the index is out of bounds.
- (nullable GSAnnotation *)objectInAnnotationsAtIndex:(NSUInteger)idx NS_SWIFT_NAME(annotation(atIndex:));

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

/// Removes the annotation at an index.
- (void)removeObjectFromAnnotationsAtIndex:(NSUInteger)idx NS_SWIFT_NAME(removeAnnotation(atIndex:));

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

/// Saves hint positions before path changes.
- (void)saveHints;

/// Restores saved hint positions after path changes.
- (void)restoreHints;

/// Updates hint indexes from their referenced nodes.
- (void)updateHints;

/// Adjusts hints after reversing paths.
- (void)adjustHintsForReversedPaths:(NSArray <GSHint *>*)hints;

#pragma mark TempData

/// Values associated with the layer for transient use.
///
/// Temporary data is not written to the font.
@property (nonatomic, strong, nullable) NSDictionary<NSString *, id> *tempData;

/// Sets or removes a temporary value.
///
/// Passing `nil` as the value removes the temporary value.
///
/// - Parameter value: The value to set, or `nil` to remove the temporary value.
- (void)setTempData:(nullable id)value forKey:(nonnull NSString *)key;

/// The temporary value for a key, or `nil` if no value is set.
- (nullable id)tempDataForKey:(nonnull NSString *)key;

#pragma mark Kerning

/// @name Kerning

/// Kerning value between a previous layer and this layer.
///
/// The value is resolved for the layer’s associated master and the requested writing direction.
/// Group kerning and kerning exceptions are considered.
/// Returns `NSNotFound` when no kerning value is defined.
///
/// - Parameter previousLayer: The layer before this layer.
- (CGFloat)previousKerningForLayer:(GSLayer *)previousLayer direction:(GSWritingDirection)direction NS_REFINED_FOR_SWIFT;

/// Kerning value between this layer and a next layer.
///
/// The value is resolved for the layer’s associated master and the requested writing direction.
/// Group kerning and kerning exceptions are considered.
/// Returns `NSNotFound` when no kerning value is defined.
///
/// - Parameter nextLayer: The layer after this layer.
- (CGFloat)nextKerningForLayer:(GSLayer *)nextLayer direction:(GSWritingDirection)direction NS_REFINED_FOR_SWIFT;

/// Sets the kerning value between a previous layer and this layer.
///
/// The value is stored for the layer’s associated master and the requested writing direction.
/// Group kerning is used unless the pair has a kerning exception.
/// Pass `CGFLOAT_MAX` to remove the kerning value.
///
/// - Parameter previousLayer: The layer before this layer.
- (void)setPreviousKerning:(CGFloat)value forLayer:(GSLayer *)previousLayer direction:(GSWritingDirection)direction NS_REFINED_FOR_SWIFT;

/// Sets the kerning value between this layer and a next layer.
///
/// The value is stored for the layer’s associated master and the requested writing direction.
/// Group kerning is used unless the pair has a kerning exception.
/// Pass `CGFLOAT_MAX` to remove the kerning value.
///
/// - Parameter nextLayer: The layer after this layer.
- (void)setNextKerning:(CGFloat)value forLayer:(GSLayer *)nextLayer direction:(GSWritingDirection)direction NS_REFINED_FOR_SWIFT;

/// Whether the pair between a previous layer and this layer has a kerning exception.
///
/// A kerning exception stores a glyph-specific value instead of using the right-side kerning group of this layer’s glyph.
///
/// - Parameter previousLayer: The layer before this layer.
- (BOOL)previousKerningExceptionForLayer:(GSLayer *)previousLayer direction:(GSWritingDirection)direction NS_SWIFT_NAME(previousKerningIsException(to:direction:));

/// Sets whether the pair between a previous layer and this layer has a kerning exception.
///
/// Enabling the exception keeps the current resolved kerning value as a glyph-specific value.
/// Disabling the exception restores use of group kerning when possible.
///
/// - Parameter previousLayer: The layer before this layer.
- (void)setPreviousKerningException:(BOOL)exception forLayer:(GSLayer *)previousLayer direction:(GSWritingDirection)direction NS_SWIFT_NAME(setPreviousKerningIsException(_:to:direction:));

/// Whether the pair between this layer and a next layer has a kerning exception.
///
/// A kerning exception stores a glyph-specific value instead of using the left-side kerning group of this layer’s glyph.
///
/// - Parameter nextLayer: The layer after this layer.
- (BOOL)nextKerningExceptionForLayer:(GSLayer *)nextLayer direction:(GSWritingDirection)direction NS_SWIFT_NAME(nextKerningIsException(to:direction:));

/// Sets whether the pair between this layer and a next layer has a kerning exception.
///
/// Enabling the exception keeps the current resolved kerning value as a glyph-specific value.
/// Disabling the exception restores use of group kerning when possible.
///
/// - Parameter nextLayer: The layer after this layer.
- (void)setNextKerningException:(BOOL)exception forLayer:(GSLayer *)nextLayer direction:(GSWritingDirection)direction NS_SWIFT_NAME(setNextKerningIsException(_:to:direction:));

#pragma mark Selection

/// @name Selection

/// The selected elements in selection order.
///
/// Selecting nodes does not add their path to the selection.
/// Setting this property to `nil` clears the selection.
@property (strong, nonatomic, null_resettable) NSOrderedSet<GSSelectableElement *> *selection;

/// The number of selected elements.
- (NSUInteger)countOfSelection;

#ifndef GLYPHS_VIEWER
/// Replaces the selection and registers the previous selection for undo.
///
/// - Parameter selection: The new selection.
- (void)setSelectionUndo:(NSOrderedSet<GSSelectableElement *> *)selection;

/// Adds an element to the selection if it is not already selected.
- (void)addSelection:(GSSelectableElement *)newSelection;

/// Adds elements to the selection, preserving their order.
- (void)addObjectsFromArrayToSelection:(NSArray<GSSelectableElement *> *)objects;

/// Replaces the selection without registering undo.
- (void)replaceSelection:(NSArray<GSSelectableElement *> *)newSelection;

/// Removes the selected element at an index.
///
/// An index outside the selection has no effect.
- (void)removeObjectFromSelectionAtIndex:(NSUInteger)idx;

/// Removes an element from the selection.
///
/// Removing a path also removes its nodes from the selection.
- (void)removeObjectFromSelection:(GSSelectableElement *)object;

/// Removes elements from the selection.
- (void)removeObjectsFromSelection:(NSArray<GSSelectableElement *> *)objects;

/// Clears the selection without registering undo.
- (void)clearSelection;

/// Clears the selection and registers the previous selection for undo.
- (void)clearSelectionUndo;

/// Whether an element is selected.
///
/// Returns `NO` for `nil`.
- (BOOL)selectionContainsObject:(nullable GSSelectableElement *)object;

#endif
/// The time of the most recent geometry invalidation.
///
/// The value uses the Core Foundation absolute time reference date.
@property NSTimeInterval lastUpdate;
#ifndef GLYPHS_VIEWER
#pragma mark updates

/// @name Updating

/// Whether updates and change notifications are deferred.
///
/// Setting this to `NO` processes accumulated updates and sends combined notifications.
@property (nonatomic, getter=areUpdatesDisabled) BOOL disableUpdates;

/// Suppresses update tracking for current and subsequent changes.
///
/// Call ``enableFutureUpdates`` before using the layer with normal update behavior.
- (void)ignoreUpdates;

/// Whether update tracking is suppressed.
- (BOOL)isIgnoringUpdates;

/// Defers updates and change notifications.
///
/// Balance each call with ``startUpdates``.
/// Update suppression is not nested.
- (void)stopUpdates;

/// Processes deferred updates and sends combined change notifications.
///
/// Call this after ``stopUpdates`` when changes made while updates were disabled must be reported.
- (void)startUpdates;

/// Resumes updates without reporting changes made while updates were disabled or ignored.
- (void)enableFutureUpdates;

/// Marks representations derived from the layer geometry as needing an update.
- (void)setNeedUpdateShapes;
#endif

/// Marks the cached side bearings as needing an update.
- (void)setNeedUpdateMetrics;

/// Marks the cached side bearings as needing an update without locking.
///
/// Use this only when external synchronization already protects metrics access.
- (void)setNeedUpdateMetricsFast;

#ifndef GLYPHS_VIEWER
/// Tolerance used when deciding whether metrics need synchronization.
///
/// - Parameter side: The metric side to evaluate.
- (CGFloat)syncMetricsThreshold:(GSMetricNumbers)side;

/// Applies all available metrics keys.
- (void)syncMetrics;

/// Applies the left side-bearing metrics key.
- (void)syncLeftMetrics;

/// Applies the advance-width metrics key.
- (void)syncWidthMetrics;

/// Applies the right side-bearing metrics key.
- (void)syncRightMetrics;

#ifndef GLYPHS_LITE
/// Applies the top side-bearing metrics key.
- (void)syncTopMetrics;

/// Applies the vertical advance-width metrics key.
- (void)syncVertWidthMetrics;

/// Applies the bottom side-bearing metrics key.
- (void)syncBottomMetrics;

/// Reinterpolates the horizontal metrics of an Alternative layer or interpolated layer.
///
/// - Parameter error: On return, an error describing why interpolation failed.
/// - Returns: `YES` when metrics were reinterpolated.
- (BOOL)reinterpolateMetrics:(NSError **)error;
#endif

/// Loads content referenced by components in preparation for subsequent access.
- (void)preloadContent;

/// Recalculates side bearings from the layer contents.
- (void)updateMetrics;

#endif

/// The master that supplies master-specific values for the layer.
///
/// For an Intermediate layer, this can be a master interpolated at the layer coordinates.
/// Returns `nil` when the layer is detached or its associated master cannot be resolved.
- (nullable GSFontMaster *)associatedFontMaster;

/// Resolves the associated master with control over the amount of interpolated master data.
///
/// For an Intermediate layer, this can return a master interpolated at the layer coordinates.
/// Returns `nil` when the layer is detached or its associated master cannot be resolved.
///
/// - Parameter thin: Whether to produce a reduced master without interpolating auxiliary master data.
- (nullable GSFontMaster *)associatedFontMasterThin:(BOOL)thin NS_SWIFT_NAME(associatedFontMaster(thin:));

/// Directly looks up the associated master by its identifier.
///
/// This method does not interpolate an Intermediate layer or fall back to another master.
- (nullable GSFontMaster *)associatedFontMasterFast;

/// Ascender value from the associated master.
@property (nonatomic, readonly) CGFloat ascender;

/// Descender value from the associated master.
@property (nonatomic, readonly) CGFloat descender;

/// Italic angle from the associated master.
@property (nonatomic, readonly) CGFloat italicAngle;

/// Slant height from the associated master.
@property (nonatomic, readonly) CGFloat slantHeight;

/// Top height from the associated master.
@property (nonatomic, readonly) CGFloat topHeight;

/// Master metric value for a metrics type.
///
/// - Parameter type: The metric type to resolve.
- (CGFloat)metricForType:(GSMetricsType)type;

/// Master metric value for a metric name.
///
/// Returns `NSNotFound` when no matching metric exists.
///
/// - Parameter key: The metric name to resolve.
- (CGFloat)metricForName:(NSString *)key NS_REFINED_FOR_SWIFT;

/// Glyph metrics for left-to-right layout.
- (GSGlyphMetrics)glyphMetrics;

/// Glyph metrics for a writing direction.
///
/// Vertical writing directions use vertical width and vertical origin when available.
///
/// - Parameter alignment: The writing direction used to resolve the metrics.
- (GSGlyphMetrics)glyphMetricsForDirection:(GSWritingDirection)alignment;

/// Vertical metrics for the associated master.
- (NSArray<GSMetricStore *> *)metrics;

/// Vertical metrics for a master.
///
/// - Parameter master: The master whose metrics should be resolved.
- (NSArray<GSMetricStore *> *)metricsForMaster:(GSFontMaster *)master;

/// Stem values for a master.
///
/// - Parameter master: The master whose stem values should be resolved.
- (NSArray<GSValueStore *> *)stemsForMaster:(GSFontMaster *)master;

/// The metrics rectangle for a writing direction.
///
/// Horizontal bounds use the layer width and vertical metrics.
/// Vertical bounds use vertical width and vertical origin when available.
///
/// - Parameter writingDirection: The writing direction used to interpret the metrics.
- (NSRect)metricsBoundsInWritingDirection:(GSWritingDirection)writingDirection;

/// The transform used to draw metrics in layer coordinates.
///
/// Italic layers return a shear transform around the slant height.
/// Returns `nil` when no transform is needed.
- (nullable NSAffineTransform *)metricsTransform;

/// Current grid settings used for layer rounding.
///
/// Returns the associated master’s rounding settings.
/// Returns `GSZeroGrid` when rounding is temporarily disabled or no associated master is available.
- (GSGridSettings)grid NS_REFINED_FOR_SWIFT;

/// Current grid settings, limiting horizontal rounding to a maximum grid length.
///
/// Returns `GSZeroGrid` when rounding is temporarily disabled or no associated master is available.
///
/// - Parameter max: The maximum horizontal grid length.
- (GSGridSettings)gridMax:(CGFloat)max NS_REFINED_FOR_SWIFT;

/// Horizontal grid length used for layer rounding.
///
/// Returns `0` when rounding is temporarily disabled or no associated master is available.
- (CGFloat)gridLengthHorizontal;

/// Vertical grid length used for layer rounding.
///
/// Returns `0` when rounding is temporarily disabled or no associated master is available.
- (CGFloat)gridLengthVertical;

/// Temporarily disables grid rounding for layer operations.
///
/// When enabled, grid accessors return zero grid values and point alignment leaves points unchanged.
@property (assign) BOOL temporarilyDisableRounding;

/// A point aligned to the layer grid for a node type.
///
/// Off-curve nodes may use a finer grid than on-curve nodes.
/// Returns the original point when rounding is temporarily disabled or no associated master is available.
///
/// - Parameter type: The node type used for alignment.
- (NSPoint)alignPointToGrid:(NSPoint)point type:(GSNodeType)type;

/// The drawable bounds of the layer.
///
/// The bounds include paths and components.
/// They do not include anchors, hints, guidelines, or other non-shape objects.
///
/// Returns `NSZeroRect` when the layer has no drawable bounds.
- (NSRect)bounds;

/// The overestimated bounds of the layer’s shapes.
///
/// For curve segments, the bounds include off-curve control points and can be larger than ``bounds``.
///
/// Returns `CGRectNull` when no shape has bounds.
- (NSRect)fastBounds;

/// Left edge of ``bounds``.
@property (readonly, nonatomic) CGFloat minX;
/// Bottom edge of ``bounds``.
@property (readonly, nonatomic) CGFloat minY;
/// Right edge of ``bounds``.
@property (readonly, nonatomic) CGFloat maxX;
/// Top edge of ``bounds``.
@property (readonly, nonatomic) CGFloat maxY;

/// The drawable bounds of the layer after applying a transform.
///
/// Passing `nil` returns ``bounds``.
///
/// - Parameter transform: The transform to apply before measuring bounds.
- (NSRect)boundsAngle:(nullable NSAffineTransform *)transform;

#ifndef GLYPHS_VIEWER
#ifndef LIBCORE

/// The bounds of the current selection.
///
/// The bounds include selected paths, components, anchors, and nodes when they belong to the layer.
///
/// Returns `CGRectNull` when the selection is empty or cannot be represented by bounds.
- (NSRect)boundsOfSelection;

/// The bounds of the current selection after applying a transform.
///
/// Returns `CGRectNull` when the selection is empty or cannot be represented by bounds.
///
/// - Parameter transform: The transform to apply before measuring bounds.
- (NSRect)boundsOfSelectionAngle:(nullable NSAffineTransform *)transform;
#endif
/// Removes empty and negligible paths and simplifies the remaining paths.
- (void)cleanUpPaths;

/// Updates node connections to match the geometry of their paths.
- (void)checkConnections;

/// Sets closed path directions according to containment and puts the outer path first.
///
/// Open paths are not changed.
- (void)correctPathDirection;

/// Finds paths whose nodes have coincident positions.
///
/// Attributes from a duplicate path are copied to the first matching path.
///
/// - Returns: The duplicate paths, without removing them from the layer.
- (NSArray<GSPath *> *)checkForCoincidentPaths;

/// Rounds selected element coordinates to a grid.
///
/// When the selection is empty, all applicable coordinates are rounded.
- (void)roundSelectedCoordinates:(GSGridSettings)grid;

/// Rounds path, component, and anchor coordinates using the associated font master settings.
- (void)roundCoordinates;

/// Rounds shape and anchor coordinates to a uniform grid and rounds the layer width to at most unit precision.
- (void)roundCoordinatesToGrid:(CGFloat)gridLength GSDeprecated("roundCoordinatesToGridWithSettings:");

/// Rounds shape and anchor coordinates to a uniform grid without normal edit bookkeeping.
- (void)roundCoordinatesToGridFast:(CGFloat)gridLength GSDeprecated("roundCoordinatesFastToGridWithSettings:");

/// Rounds path, component, and anchor coordinates to a grid.
- (void)roundCoordinatesToGridWithSettings:(GSGridSettings)grid;

/// Rounds path, component, and anchor coordinates to a grid without normal edit bookkeeping.
- (void)roundCoordinatesFastToGridWithSettings:(GSGridSettings)grid;

/// Adds nodes at extrema of all path segments where needed.
- (void)addExtremePoints;

/// Adds nodes at extrema of path segments.
///
/// - Parameter force: Whether to add extrema even when they would make a segment unsuitable.
/// - Parameter checkSelection: Whether to process only selected path segments.
- (void)addExtremePointsForce:(BOOL)force checkSelection:(BOOL)checkSelection;

#ifndef GLYPHS_LITE
/// Adds nodes at inflection points of cubic segments.
- (void)addInflectionPoints;
#endif

/// Points where a line segment intersects the outlines of the layer.
///
/// The returned point array includes `startPoint` and `endPoint`.
///
/// - Parameter startPoint: The start of the line segment in layer coordinates.
/// - Parameter endPoint: The end of the line segment in layer coordinates.
- (FTPointArray *)calculateIntersectionsStartPoint:(NSPoint)startPoint endPoint:(NSPoint)endPoint;

/// Points where a line segment intersects a path.
///
/// The returned point array does not include the line endpoints unless they intersect the path.
/// Use ``GSPath/intersectionsWithLineFrom:to:`` instead.
///
/// - Parameter path: The path to inspect.
/// - Parameter startPoint: The start of the line segment in layer coordinates.
/// - Parameter endPoint: The end of the line segment in layer coordinates.
- (FTPointArray *)calculateIntersectionsForPath:(GSPath *)path startPoint:(NSPoint)startPoint endPoint:(NSPoint)endPoint GSDeprecated("[path intersectionsWithLineFrom:to:]");

/// Points where a line segment intersects the outlines of the layer.
///
/// The returned point array includes `startPoint` and `endPoint`.
///
/// - Parameter startPoint: The start of the line segment in layer coordinates.
/// - Parameter endPoint: The end of the line segment in layer coordinates.
/// - Parameter decompose: Whether to decompose components before calculating intersections.
/// - Parameter ignoreLocked: Whether to exclude locked and out-of-focus shapes.
/// - Parameter clipToBound: Whether to limit the inspected line segment to the layer bounds.
- (FTPointArray *)calculateIntersectionsStartPoint:(NSPoint)startPoint endPoint:(NSPoint)endPoint decompose:(BOOL)decompose ignoreLocked:(BOOL)ignoreLocked clipToBound:(BOOL)clipToBound;

/// Points where a line segment intersects the outlines of the layer.
///
/// The returned point array includes `startPoint` and `endPoint`.
/// `ignoreComponents` applies only when `decompose` is `NO`.
///
/// - Parameter startPoint: The start of the line segment in layer coordinates.
/// - Parameter endPoint: The end of the line segment in layer coordinates.
/// - Parameter decompose: Whether to decompose components before calculating intersections.
/// - Parameter ignoreLocked: Whether to exclude locked and out-of-focus shapes.
/// - Parameter clipToBound: Whether to limit the inspected line segment to the layer bounds.
/// - Parameter ignoreComponents: Whether to exclude component shapes.
- (FTPointArray *)calculateIntersectionsStartPoint:(NSPoint)startPoint endPoint:(NSPoint)endPoint decompose:(BOOL)decompose ignoreLocked:(BOOL)ignoreLocked clipToBound:(BOOL)clipToBound ignoreComponents:(BOOL)ignoreComponents;

#ifndef LIBCORE
#ifndef GLYPHS_LITE
/// Points where segments of the layer outlines intersect each other.
///
/// The returned point array may contain the same point more than once.
/// Treat the point array as read-only.
- (FTPointArray *)intersections;
#endif
#endif
#ifndef GLYPHS_VIEWER
/// The undo manager of the containing glyph when the layer belongs to that glyph.
@property (nonatomic, readonly, nullable) NSUndoManager *undoManager;

/// The undo manager of the containing glyph when undo registration is enabled.
///
/// Returns `nil` when the layer does not belong to the glyph or undo registration is disabled.
@property (nonatomic, readonly, nullable) NSUndoManager *undoManagerCheck;

#endif
#endif

#ifndef LIBCORE
#ifndef GLYPHS_VIEWER

/// Content representing the selected elements for serialization and paste.
///
/// Partial path selections are represented as open path fragments.
/// Related corner and cap hints are included when their complete attachment is selected.
- (NSDictionary *)selectedObjects;

/// Structural mappings for the selected elements.
///
/// The mappings identify nodes and shapes by position and other elements by stable keys.
/// They can be applied to layers with corresponding content.
- (NSDictionary *)selectedElementsMappings;

/// Removes elements described by structural mappings.
///
/// The mappings must refer to corresponding content in the receiver.
///
/// > Important: Mappings go out of sync if the layer contents were edited since creating the mapping.
/// > Use this method only when no such mutations took place.
///
/// - Parameter selectedElementsMappings: Mappings produced by ``selectedElementsMappings``.
- (void)removeMappedElements:(NSDictionary *)selectedElementsMappings;

/// Removes a selectable element from the layer.
///
/// Removing the last node of a path also removes the path.
- (void)removeElement:(GSSelectableElement *)element;

/// Removes mapped nodes while preserving the unselected path portions.
///
/// Paths are split where necessary, and hints attached to removed nodes are removed.
///
/// - Parameter indexPaths: Node index paths from ``selectedElementsMappings``.
- (void)removeNodesCutPath:(NSArray <NSIndexPath *>*)indexPaths;

/// Adds content from a serialized selection dictionary.
///
/// The added elements become the selection.
///
/// - Parameter dictionary: A decoded selection dictionary.
/// - Parameter format: The format version of the dictionary.
/// - Parameter selectedNodes: Target nodes used to attach pasted hints.
/// - Parameter error: On failure, information about the error.
/// - Returns: Whether the complete selection was added.
- (BOOL)pasteDict:(NSDictionary *)dictionary format:(GSFormatVersion)format selectedNodes:(NSArray<GSNode *> *)selectedNodes error:(NSError **)error;

/// Separates paths by whether they contain selected nodes.
///
/// When no path contains a selected node, the last path is treated as selected.
///
/// - Parameter selectedPaths: The array to which paths containing selected nodes are added.
/// - Parameter notSelectedPaths: The array to which the remaining paths are added.
- (void)selectedPaths:(NSMutableArray *)selectedPaths notSelected:(nullable NSMutableArray *)notSelectedPaths;

/// Encodes layer elements as Glyphs property-list data.
///
/// - Parameter dict: A dictionary containing model objects grouped by layer element keys.
/// - Parameter format: The Glyphs file format version to use.
/// - Parameter error: On failure, information about the error.
/// - Returns: The encoded data, or `nil` when an element cannot be encoded.
+ (nullable NSData *)layerDataFromDict:(NSDictionary<NSString *, id> *)dict format:(GSFormatVersion)format error:(NSError **)error;

#endif
#endif

#ifndef GLYPHS_VIEWER

/// Applies an affine transform to the selected elements.
///
/// When the selection is empty, the complete layer is transformed.
- (void)transformSelection:(NSAffineTransform *)transform;

/// Applies an affine transform to editable layer content.
///
/// - Parameter checkForSelection: Whether to restrict the operation to selected elements when the selection is not empty.
- (void)transform:(NSAffineTransform *)transform checkForSelection:(BOOL)checkForSelection;

/// Applies an affine transform to editable layer content.
///
/// - Parameter checkForSelection: Whether to restrict the operation to selected elements when the selection is not empty.
/// - Parameter doComponents: Whether to transform component shapes.
- (void)transform:(NSAffineTransform *)transform checkForSelection:(BOOL)checkForSelection doComponents:(BOOL)doComponents; // compatibility with plugins

/// Applies an affine transform to editable layer content and conditionally transforms components.
///
/// - Parameter checkForSelection: Whether to restrict the operation to selected elements when the selection is not empty.
/// - Parameter componentCallback: A block that returns whether a component or component hint should be transformed.
- (void)transform:(NSAffineTransform *)transform checkForSelection:(BOOL)checkForSelection doComponent:(nullable BOOL (^)(GSComponent *__nullable component))componentCallback;

/// Applies an affine transform to the complete layer without normal edit bookkeeping.
- (void)transformFast:(NSAffineTransform *)transform;

/// Applies an affine transform to the complete layer and conditionally transforms components without normal edit bookkeeping.
///
/// - Parameter componentCallback: A block that returns whether a component or component hint should be transformed.
- (void)transformFast:(NSAffineTransform *)transform doComponent:(nullable BOOL (^)(GSComponent *__nullable component))componentCallback;

/// Shifts layer content horizontally while compensating components for shifts of their base glyphs.
///
/// - Parameter shift: The horizontal displacement in layer coordinates.
/// - Parameter otherShifts: Base glyph names mapped to their simultaneous displacement vectors.
- (void)horizontalShift:(CGFloat)shift otherShifts:(nullable NSDictionary<NSString *, NSValue *> *)otherShifts;

/// Shifts layer content vertically while compensating components for shifts of their base glyphs.
///
/// - Parameter shift: The vertical displacement in layer coordinates.
/// - Parameter otherShifts: Base glyph names mapped to their simultaneous displacement vectors.
- (void)verticalShift:(CGFloat)shift otherShifts:(nullable NSDictionary<NSString *, NSValue *> *)otherShifts;
#endif

/// Creates an independent copy with the requested optional content.
///
/// Shapes, anchors, hints, metrics, attributes, and user data are always copied.
/// Options control additional content such as the background, guides, annotations, and drawing state.
///
/// - Parameter options: The content and initialization behavior to use for the copy.
- (instancetype)copyWithOptions:(GSGlyphCopyOptions)options;

/// Creates an independent copy with the requested optional content and selection handling.
///
/// Selection is included only when the options include ``GSGlyphCopyExtras``.
///
/// - Parameter options: The content and initialization behavior to use for the copy.
/// - Parameter selection: Whether selected elements are mapped to the corresponding elements in the copy.
- (instancetype)copyWithOptions:(GSGlyphCopyOptions)options selection:(BOOL)selection;

/// Emits the layer to a pen.
///
/// Open and closed paths are emitted to the same pen.
/// Full-color layers include their paint when the pen supports it.
- (void)drawInPen:(NSObject<GSPenProtocol> *)pen;

/// Emits the layer to separate pens for closed and open paths.
///
/// Pass `nil` for `openPen` to omit open paths.
/// Full-color layers include their paint when `pen` supports it.
///
/// - Parameter pen: The pen that receives closed paths and components.
/// - Parameter openPen: The pen that receives open paths.
- (void)drawInPen:(NSObject<GSPenProtocol> *)pen openPen:(nullable NSObject<GSPenProtocol> *)openPen;

/// Emits the layer’s shapes without layer-level paint processing.
- (void)drawSimpleInPen:(NSObject<GSPenProtocol> *)pen;

/// Emits the layer without full-color paint processing.
///
/// Shape stroke attributes are applied when supported by the pen.
/// Pass `nil` for `openPen` to omit open paths.
///
/// - Parameter pen: The pen that receives closed paths and components.
/// - Parameter openPen: The pen that receives open paths.
- (void)drawBlackInPen:(NSObject<GSPenProtocol> *)pen openPen:(nullable NSObject<GSPenProtocol> *)openPen;

#ifndef GLYPHS_LITE
/// Emits the painted drawing operations of a full-color layer to a pen.
- (void)drawColorInPen:(NSObject<GSPenProtocol> *)pen;

/// Emits the path and error operations of a draw stack to a pen.
///
/// - Parameter drawStack: The drawing operations to emit.
/// - Parameter pen: The pen that receives the operations and their paint attributes.
- (void)drawDrawStack:(NSArray<GSDrawSlice *> *)drawStack inPen:(NSObject<GSPenProtocol> *)pen;

/// Maps axis IDs from the parent `partsSettings` to a value corresponding to `GSPartSetting` (or `nil`, for `GSPartSettingNone`).
//@property (nonatomic, strong) NSMutableDictionary<NSString *, NSNumber *> *partSelection;

/// The effective color used to represent the layer, or `nil` when no color is configured.
///
/// Palette color layers use their color entry from the first palette.
/// Other layers use the color configured for the associated font master, preferring its dark-appearance variant when requested.
///
/// - Parameter dark: Whether to prefer a color for a dark appearance.
- (nullable NSColor *)layerColor:(BOOL)dark;

/// The layer’s palette color from the given palette, or `nil` when it cannot be resolved.
///
/// A palette color entry of `*` resolves to the text color.
///
/// - Parameter paletteIdx: The index of the palette.
- (nullable NSColor *)paletteColor:(NSUInteger)paletteIdx;

/// The effective color palettes for the layer, or `nil` when none are configured.
///
/// Palettes on the associated font master take precedence over palettes on the font and its first master.
- (nullable GSPalettes *)paletteInfo;
#endif
#pragma mark Attributes

/// Values that configure layer behavior and special layer formats.
///
/// Attribute values must support property-list encoding.
@property (strong, nonatomic, nullable) NSDictionary<GSLayerAttribute, id> *attributes;

/// The number of attributes.
- (NSUInteger)countOfAttributes;

/// The value for an attribute key, or `nil` if no value is set.
- (nullable id)attributeForKey:(GSLayerAttribute)key;

/// Replaces all attributes without registering undo or notifying the glyph.
///
/// The mutable dictionary becomes the layer’s attribute storage.
- (void)setAttributesFast:(NSMutableDictionary<GSLayerAttribute, id> *)attributes;

/// Sets or removes an attribute.
///
/// Setting an attribute registers undo and notifies the glyph.
/// Passing `nil` as the value removes the attribute.
///
/// - Parameter value: A value that supports property-list encoding, or `nil` to remove the attribute.
- (void)setAttribute:(nullable id)value forKey:(GSLayerAttribute)key;

/// Sets an attribute without registering undo or notifying the glyph.
- (void)setAttributeFast:(id)value forKey:(GSLayerAttribute)key;

/// Removes an attribute.
- (void)removeAttributeForKey:(GSLayerAttribute)key;

/// Whether axis values satisfy all bounds in a set of Alternative-layer rules.
///
/// Bounds are inclusive.
/// Rules for which no axis value is supplied are ignored.
///
/// - Parameter rule: Axis rules keyed by axis ID.
/// - Parameter axisValues: Internal axis values keyed by axis ID.
+ (BOOL)rule:(NSDictionary<NSString *, NSDictionary<NSString *, NSNumber *> *> *)rule matchesCoordinates:(nullable GSAxisValues *)axisValues;

/// Values deferred until the layer can complete format-dependent decoding.
@property (nonatomic, strong, nullable) NSDictionary *readBuffer;

/// Stores a value for format-dependent decoding after the layer hierarchy is available.
///
/// - Parameter values: The deferred value.
/// - Parameter key: The key used by the decoding step.
- (void)setReadBuffer:(NSObject *)values forKey:(NSString *)key;

/// Completes format-dependent decoding after the layer has been attached to its object hierarchy.
///
/// - Parameter error: On failure, information about the error.
/// - Parameter formatVersion: The Glyphs file format version from which the layer was decoded.
/// - Returns: Whether decoding completed successfully.
- (BOOL)postRead:(NSError **)error format:(GSFormatVersion)formatVersion;

/// Whether a metric is secondary for the layer’s glyph case.
///
/// x-height is secondary for uppercase glyphs.
/// Cap height is secondary for lowercase and small-cap glyphs.
///
/// - Parameter metrics: The metric to evaluate.
- (BOOL)isSecondaryMetric:(GSMetricStore *)metrics;

#pragma GSAutoCodeStart methods

/// Whether the layer participates in interpolation and export.
@property (nonatomic) BOOL active;

/// A custom color label, or `nil` when the layer has no custom color label.
///
/// Setting a custom color clears ``colorIndex``.
@property (nonatomic, strong, nullable) NSColor *color;

/// A standard color label, or ``GSLabelColor/noColor`` when no standard label is assigned.
///
/// Setting a standard color clears ``color``.
@property (nonatomic) GSLabelColor colorIndex;

#pragma mark Guides

/// Layer-local guides.
///
/// Global guides are stored on the associated font master.
@property (nonatomic, strong) NSArray<GSGuide *> *guides;

/// The number of layer-local guides.
- (NSUInteger)countOfGuides;

/// The guide at an index.
///
/// - Returns: The guide, or `nil` if the index is out of bounds.
- (nullable GSGuide *)objectInGuidesAtIndex:(NSUInteger)idx NS_SWIFT_NAME(guide(atIndex:));

/// The index of a guide in the layer.
///
/// - Returns: The guide index, or `NSNotFound` if the guide is not in the layer.
- (NSUInteger)indexOfObjectInGuides:(nonnull GSGuide *)guide NS_REFINED_FOR_SWIFT;

/// Adds a guide.
- (void)addGuide:(nonnull GSGuide *)guide;

/// Inserts a guide at an index.
- (void)insertObject:(nonnull GSGuide *)guide inGuidesAtIndex:(NSUInteger)idx NS_SWIFT_NAME(insertGuide(_:atIndex:));

/// Removes a guide.
- (void)removeObjectFromGuides:(nonnull GSGuide *)guide NS_SWIFT_NAME(removeGuide(_:));

/// Removes the guide at an index.
- (void)removeObjectFromGuidesAtIndex:(NSUInteger)idx NS_SWIFT_NAME(removeGuide(atIndex:));

#pragma mark UserData

/// Persistent values associated with the layer by API clients.
///
/// User-data values must support property-list encoding.
@property (strong, nonatomic, nullable) NSDictionary<NSString *, id> *userData;

/// The user-data value for a key, or `nil` if no value is set.
- (nullable NSObject *)userDataForKey:(NSString *)key;

/// Sets or removes a persistent user-data value.
///
/// Changing user data registers undo.
/// Passing `nil` as the value removes the user-data value.
///
/// - Parameter value: A value that supports property-list encoding, or `nil` to remove the user-data value.
- (void)setUserData:(nullable id)value forKey:(nonnull NSString *)key;

/// Removes a user-data value.
- (void)removeUserDataForKey:(NSString *)key;
#pragma GSAutoCodeEnd methods

@end
NS_ASSUME_NONNULL_END
