//
//  GSDrawSlice.h
//  GlyphsCore
//
//  Created by Florian Pircher on 20.04.23.
//  Copyright © 2023 schriftgestaltung.de. All rights reserved.
//

#import <GlyphsCore/GSPenProtocol.h>

NS_ASSUME_NONNULL_BEGIN

@class GSDrawPenSlice;
@class GSImage;
@class GSTextElement;
@class GSDrawSliceMask;

typedef NS_ENUM(uint8_t, GSDrawSliceError) {
	GSDrawSliceErrorComponentMissingBaseGlyph = 1,
	GSDrawSliceErrorComponentEmptyBaseGlyph = 2,
	GSDrawSliceErrorComponentBadReference = 3,
};

@interface GSDrawSlice<__covariant PenType> : NSObject<NSCopying>

@property (strong, nullable) NSDictionary<GSShapeKey, id> *attributes;
/// The transform to be applied to the slice as it is rendered.
///
/// This transform is defined in terms of the coordinate system as transformed by any containing groups.
/// That is, the effective transform of the slice is the concatenation of all the transforms of any groups that contain the slice and the transform of the slice itself.
///
/// A `nil` transform represents the identity transform.
@property (strong, nullable) NSAffineTransform *transform;
/// Requires ``GSDrawStackFeatureMasks``.
@property (strong, nullable) NSArray<GSDrawSliceMask *> *masks;
/// The reference rect used to fill gradients and apply other effects that require bounds to be unaffected by masks, strokes, and other externalities.
///
/// The getter pre-applies the transform of the slice.
///
/// `CGRectNull` if there is no concrete reference rect for the slice.
@property (assign, nonatomic) CGRect referenceRect;

/// The bounds of the slice.
///
/// Provided ``GSDrawStackFeatureMasks`` is supported, masks do not affect these bounds.
///
/// `CGRectNull` if there are no concrete bounds for the slice.
- (CGRect)bounds;

/// A rectangle containing the bounds of the slice.
///
/// The implementation of this method may be more efficient than `-bounds`.
/// For example, a Bézier path may report the bounds of its nodes instead of computing the bounds of its curves.
///
/// `CGRectNull` if there are no concrete bounds for the slice.
- (CGRect)overestimatedBounds;

@end

@interface GSDrawPenSlice<__covariant PenType> : GSDrawSlice

@property (strong) PenType pen;

- (instancetype)initWithPen:(PenType)pen;

@end

@interface GSDrawComponentSlice : GSDrawSlice

@property (strong) GSComponent *component;

- (instancetype)initWithComponent:(GSComponent *)component;

@end

@interface GSDrawImageSlice : GSDrawSlice

@property (strong) GSImage *image;

- (instancetype)initWithImage:(GSImage *)image;

@end

@interface GSDrawTextSlice : GSDrawSlice

@property (strong) GSTextElement *textElement;

- (instancetype)initWithTextElement:(GSTextElement *)textElement;

@end

@interface GSDrawErrorSlice : GSDrawSlice

@property (assign) GSDrawSliceError error;

- (instancetype)initWithError:(GSDrawSliceError)error;

@end

@interface GSDrawGroupSlice : GSDrawSlice

@property (strong) NSArray<GSDrawSlice *> *slices;

- (instancetype)initWithSlices:(NSArray<GSDrawSlice *> *)slices;

@end

@interface GSDrawSliceMask : NSObject

@property (strong) GSShape *shape;
/// A regular mask behaves like a mask in Photoshop or a clip path in PostScript-style system (e.g., Core Graphics, SVG).
/// An inverse mask masks the inverse area like an inverted mask in Photoshop or a SwiftUI clip path with the inverse option set.
@property (assign) GSMaskType maskType;

- (instancetype)initWithShape:(GSShape *)shape type:(GSMaskType)maskType;

@end

NS_ASSUME_NONNULL_END
