//
//  GSComponent.h
//  Glyphs
//
//  Created by Georg Seifert on 30.3.08.
//  Copyright 2008 schriftgestaltung.de. All rights reserved.
//

#import <GlyphsCore/GSTransformableElement.h>
#import <GlyphsCore/GSDrawSlice.h>

NS_ASSUME_NONNULL_BEGIN

@class GSGlyph;
@class GSFont;
@class GSLayer;
@class GSAxis;

#ifndef GLYPHS_VIEWER
@class GSNotifyingDictionary;
#else
#define GSNotifyingDictionary NSMutableDictionary
#endif

/// A reference to another glyph placed as a shape in a glyph layer.
///
/// - The *base glyph* is the glyph referenced by the component.
/// - The *component layer* is the specific layer that is referenced.
/// - The *containing layer* is the layer in which the component is  used as a shape.
///
/// Components can use automatic alignment to determine their position in the containing layer.
/// There are different kinds of automatic alignment, see ``GSComponentAlignment``.
///
/// Automatic positioning can also be affected by using an anchor to snap a component into place.
/// This mirrors the use of anchors in OpenType layout for mark positioning and cursive attachment.
///
/// *Smart Components* are components that reference a *Smart Glyph*.
/// A Smart Glyph defines glyph-local axes that can be used to alter components of the glyph.
///
/// A component references a glyph by its glyph name.
/// This can cause an invalid state in the following situations:
///
/// - The referenced glyph does not exist (no base glyph).
/// - The component references itself, causing an infinite reference cycle (bad reference).
/// - The referenced layer does not contain an outline (empty base glyph).
@interface GSComponent : GSTransformableElement {
	__unsafe_unretained GSGlyph * _component;
	GSComponentAlignment _isAligned;
	GSLayer *_cachedLayer;
	NSDate *_lastChange;
  @public
	NSString *_componentName;
	NSString *_componentMasterId;
	GSComponentAlignment _alignment;

#pragma GSAutoCodeStart ivars

	NSString *_anchor;
	CGFloat _keepWeight;
	BOOL _traverseAnchors;

#pragma GSAutoCodeEnd ivars
}

/// Creates a component that references a base glyph by name.
///
/// The base glyph is resolved in the font of the containing layer after the component is inserted into a layer.
///
/// - Parameter componentName: The name of the base glyph.
- (instancetype)initWithComponentName:(NSString *)componentName;

/// Whether another component has matching placement and selected configuration values.
///
/// The comparison includes position, scale, angle, slant, orientation, automatic-positioning policy, lock state, base-glyph name and master ID, attachment anchor, anchor traversal, smart-component settings, and shape attributes.
/// It does not compare the resolved base-glyph contents or containing layers.
- (BOOL)isEqualToComponent:(GSComponent *)other;

/// The name of the referenced base glyph.
///
/// The name is resolved in the font of the containing layer.
@property (strong, nonatomic, nullable) NSString *componentName;

/// The ID of the base-glyph master to use.
///
/// `nil` uses the master associated with the containing layer.
@property (strong, nonatomic, nullable) NSString *componentMasterId;

/// The base glyph resolved from ``componentName`` in the font of the containing layer.
///
/// Returns `nil` when the component has no containing font or the font has no glyph with the referenced name.
/// Set ``componentName`` to change the base glyph.
@property (unsafe_unretained, readonly, nullable) GSGlyph *component;

/// The result of the most recent automatic positioning.
///
/// `GSAlignmentNoAligned` indicates that the component was not positioned automatically.
/// `GSAlignmentDefault` indicates that automatic positioning has not been evaluated.
/// Use ``alignment`` to configure automatic positioning.
@property (nonatomic) GSComponentAlignment effectiveAlignment;

/// Whether the component participates in automatic base-glyph alignment under the current policy.
@property (nonatomic, readonly) BOOL doesAlign;

/// Whether the component and containing glyph normally qualify for automatic base-glyph alignment.
///
/// This value does not account for ``alignment``.
@property (nonatomic, readonly) BOOL shouldAlign;

/// Whether the component participates in automatic anchor attachment under the current policy.
@property (nonatomic, readonly) BOOL doesAttach;

/// Whether the component normally qualifies for automatic anchor attachment.
///
/// This value does not account for ``alignment``.
@property (nonatomic, readonly) BOOL shouldAttach;

/// The coordinate mode used for automatic base-glyph alignment.
///
/// Returns `GSAlignmentHorizontal` for horizontal-only alignment and `GSAlignmentAligned` for alignment in both coordinates.
@property (nonatomic, readonly) GSComponentAlignment alignmentType;

/// Explicit interpolation values for smart-component axes.
///
/// Keys are axis IDs from the referenced glyph and values are numbers.
/// An axis without an explicit value uses the coordinate of the corresponding base-glyph layer.
/// Preserve unrecognized entries when modifying the dictionary.
@property (strong, nonatomic, nullable) GSNotifyingDictionary *pieceSettings; // TODO: rename to a more axis-leaning term?

#ifndef GLYPHS_LITE
/// Replaces the smart-component settings without copying, undo, or change notifications.
///
/// The dictionary must be a `GSNotifyingDictionary` and must not be modified for another owner.
/// The caller is responsible for any required updates.
- (void)setPieceSettingsFast:(GSNotifyingDictionary *)pieceSettings;

/// Sets an explicit interpolation value for a smart-component axis.
///
/// - Parameter key: The axis ID.
- (void)setPieceValue:(CGFloat)value forKey:(NSString *)key;

/// Removes an explicit interpolation value for a smart-component axis.
///
/// The axis then uses the coordinate of the corresponding base-glyph layer.
///
/// - Parameter key: The axis ID.
- (void)removePieceValueForKey:(NSString *)key;

/// The explicit interpolation value for a smart-component axis.
///
/// Returns `(CGFloat)NSNotFound` when the axis has no explicit value.
///
/// - Parameter key: The axis ID.
- (CGFloat)pieceValueForKey:(NSString *)key NS_REFINED_FOR_SWIFT;

#ifndef GLYPHS_VIEWER
/// The name of the model syllable selected for the component’s Hangul position.
///
/// The component name determines the initial consonant, vowel, or final consonant position.
/// The containing glyph’s Unicode and the font’s `Hangul Composition Groups` custom parameter determine the model syllable.
/// The returned name does not guarantee that the font contains the model glyph.
///
/// Returns `nil` when no model syllable applies.
- (nullable NSString *)hangulModelGlyph;

/// Whether the containing glyph is the model syllable selected for the component’s Hangul position.
///
/// Only components in glyphs classified as Hangul syllables qualify.
- (BOOL)isHangulKeyComponent;
#endif
#endif

/// The bounds of the transformed component representation.
///
/// The bounds include open paths and any placeholder used for an unresolved, recursive, or empty base glyph.
///
/// Returns `CGRectNull` when the component has no representation.
- (NSRect)bounds;

/// The transformed closed paths of the component layer and its nested components.
///
/// The result includes a placeholder for an unresolved, recursive, or empty base glyph.
///
/// Returns `nil` when the component has no base-glyph name.
- (nullable NSBezierPath *)bezierPath;

/// The transformed closed paths of the component layer and its nested components.
///
/// Ignoring placeholders omits unresolved and recursive references and returns an empty path for an empty base glyph.
- (nullable NSBezierPath *)bezierPathIgnorePlaceholder:(BOOL)ignorePlaceholder;

/// Collects the transformed closed paths while traversing a component hierarchy.
///
/// Use ``bezierPath`` or ``bezierPathIgnorePlaceholder:`` unless sharing traversal state across components.
///
/// - Parameter ignorePlaceholder: Whether to omit placeholder representations.
/// - Parameter seenComponents: The base-glyph names currently being traversed.
/// - Parameter hasPaths: On return, whether the resolved hierarchy contains any paths, including open paths.
/// - Returns: The collected path, or `nil` when no representation can be resolved.
- (nullable NSBezierPath *)bezierPathIgnorePlaceholder:(BOOL)ignorePlaceholder seen:(NSMutableSet *)seenComponents hasPaths:(BOOL *)hasPaths;

/// The transformed open paths of the component layer and its nested components.
///
/// Placeholder representations are not included.
/// Returns `nil` when no component layer can be resolved and an empty path when the hierarchy has no open paths.
- (nullable NSBezierPath *)openBezierPath;

/// Collects the transformed open paths while traversing a component hierarchy.
///
/// Use ``openBezierPath`` unless sharing traversal state across components.
///
/// - Parameter seenComponents: The base-glyph names currently being traversed.
/// - Returns: The collected path, or `nil` when no component layer can be resolved.
- (nullable NSBezierPath *)openBezierPath:(NSMutableSet *)seenComponents;

/// Resolves the base glyph and updates the reference when the glyph was renamed.
///
/// A renamed glyph causes normal component change notifications.
- (void)updateComponentGlyph;

/// Resolves the base glyph without registering a change to the component.
///
/// The containing layer is still marked for a metrics update when the referenced glyph was renamed.
- (void)updateComponentGlyphDontNotify;

/// Resolves the base glyph without undo or change notifications.
///
/// The component must belong to a layer in a font.
/// The caller is responsible for any required updates.
- (void)updateComponentGlyphFast;

/// The applicable layer of the base glyph.
///
/// An explicit master selection overrides the master associated with the containing layer.
/// Smart component settings may produce an interpolated layer.
/// The component transform is not applied to this layer.
/// Returns `nil` when no applicable layer can be resolved.
- (nullable GSLayer *)componentLayer;

#ifndef GLYPHS_VIEWER

/// Replaces the component in its containing layer with transformed copies of the base-glyph layer contents.
///
/// Nested components remain components.
/// Applicable anchors and hints are transferred to the containing layer.
- (void)decompose;

/// Detached paths produced by flattening the transformed component contents.
///
/// Nested components and shape effects are flattened without modifying the component or its containing layer.
///
/// - Parameter removeOverlap: Whether to remove overlaps from the flattened paths.
- (NSArray<GSPath *> *)decomposedPathsRemovingOverlap:(BOOL)removeOverlap;

#ifndef GLYPHS_LITE

/// Whether the closed-path representation crosses that of another component.
///
/// Open paths and containment without an outline crossing are not considered intersections.
- (BOOL)intersects:(GSComponent *)otherComponent;

/// Whether the closed-path representation crosses that of another component without first comparing their bounds.
///
/// Use this method when the caller has already established that the component bounds intersect.
- (BOOL)intersectsDontCheckBounds:(GSComponent *)otherComponent;

/// Intersections between the transformed component representation and a line segment.
///
/// Closed paths, open paths, and any placeholder representation are included.
/// Returns an empty point array when there are no intersections.
- (FTPointArray *)intersectionsWithLineFrom:(NSPoint)startPoint to:(NSPoint)endPoint;

#endif

/// Moves the component to the beginning of its containing layer’s shape order.
///
/// The component becomes selected.
/// Does nothing when the component is not contained in its parent layer.
- (void)makeFirst;

/// Enables automatic positioning.
///
/// Uses the default policy when the component normally qualifies for alignment or attachment and forces alignment otherwise.
- (void)enableAlignment;

#endif
#ifndef GLYPHS_LITE
/// The effective interpolation value for a smart-component axis.
///
/// An explicit value in ``pieceSettings`` overrides the coordinate of the corresponding base-glyph layer.
///
/// - Parameter axis: An axis of the referenced glyph.
- (CGFloat)valueForAxis:(GSAxis *)axis;
#endif
#ifndef GLYPHS_VIEWER

/// Prepares an interpolated smart-component layer for later access.
///
/// Does nothing when the component does not require smart-component interpolation.
- (void)preloadCachedLayers;
#endif

#pragma GSAutoCodeStart methods

/// The policy for automatic positioning.
///
/// - `GSAlignmentDefault` follows normal alignment and attachment eligibility.
/// - `GSAlignmentDisable` disables both.
/// - `GSAlignmentForce` overrides normal alignment eligibility.
/// - `GSAlignmentHorizontal` uses normal eligibility but restricts base-glyph alignment to the horizontal coordinate.
///
/// Access ``effectiveAlignment`` for the alignment that is currently applied to the component.
@property (nonatomic) GSComponentAlignment alignment;

/// The base anchor name to use for automatic attachment.
///
/// The corresponding anchor in the component layer normally has the same name prefixed with an underscore.
/// `nil` selects a matching anchor pair automatically.
@property (nonatomic, strong, nullable) NSString *anchor;

/// Whether anchor traversal includes anchors from the component layer.
///
/// Disabling traversal prevents these anchors from being used to attach later components.
@property (nonatomic) BOOL traverseAnchors;

/// A new placeholder path for an unresolved base glyph.
///
/// The returned path can be modified independently.
+ (NSBezierPath *)newMissingBaseGlyphPath;

/// A new placeholder path for a base-glyph hierarchy without paths.
///
/// The returned path can be modified independently.
+ (NSBezierPath *)newEmptyBaseGlyphPath;

/// A new placeholder path for a recursive component reference.
///
/// The returned path can be modified independently.
+ (NSBezierPath *)newBadReferencePath;

#pragma GSAutoCodeEnd methods

#ifndef GLYPHS_VIEWER
/// Sets the automatic-positioning policy without undo, change notifications, or layer updates.
///
/// The caller is responsible for any required updates.
///
/// - Parameter flag: The alignment policy to set.
- (void)setAlignmentFast:(GSComponentAlignment)flag;
#endif

#pragma mark - Deprecated (Glyphs 3 compatibility)

- (void)makeEnableAlignment GSDeprecated("enableAlignment");
- (void)makeDisableAlignment GSDeprecated("setAlignment:");
- (void)makeForceAlignment GSDeprecated("setAlignment:");

@end
NS_ASSUME_NONNULL_END
