//
//  GSShape.h
//  GlyphsCore
//
//  Created by Georg Seifert on 06.04.19.
//  Copyright © 2019 schriftgestaltung.de. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <GlyphsCore/GSBase.h>
#import <GlyphsCore/GSContainerProtocol.h>
#import <GlyphsCore/GSSelectableElementProtocol.h>

typedef enum : unsigned short {
	GSShapeTypePath = 1 << 1,
	GSShapeTypeComponent = 1 << 2,
	GSShapeTypeGroup = 1 << 3,
} GSShapeType;

typedef NS_ENUM(NSUInteger, GSLineCapStyle) {
	GSLineCapStyleButt = 0,
	GSLineCapStyleRound = 1,
	GSLineCapStyleRoundInset = 2,
	GSLineCapStyleSquare = 3,
	GSLineCapStyleAlignToAxis = 4,
};

NS_ASSUME_NONNULL_BEGIN

extern NSString *const GSPathFillColorKey;
extern NSString *const GSPathFillKey;
extern NSString *const GSPathFillPatternFileKey;
extern NSString *const GSPathFillPatternBlendModeKey;
extern NSString *const GSPathFillPatternOffsetKey;
extern NSString *const GSPathFillPatternScaleKey;
extern NSString *const GSPathFillPatternAngleKey;
extern NSString *const GSPathGradientKey;
extern NSString *const GSPathStrokeColorKey;
extern NSString *const GSPathStrokeWidthKey;
extern NSString *const GSPathStrokeHeightKey;
extern NSString *const GSPathStrokePositionKey;
extern NSString *const GSPathStrokeLineCapStartKey;
extern NSString *const GSPathStrokeLineCapEndKey;
extern NSString *const GSPathStrokeLineJoinKey;
extern NSString *const GSPathShadowKey;
extern NSString *const GSPathInnerShadowKey;
extern NSString *const GSPathNoiseKey;
extern NSString *const GSShapeMaskKey;
extern NSString *const GSComponentReversePathsKey;

extern NSString *const GSAttributesKey;
extern NSString *const GSPathKey;

extern NSString *const GSPathMergeOpenPaths;

extern NSString *const GSPathFocusKey;
extern NSString *const GSShapeIdentifier;
extern NSString *const GSPieceSettingsGroup;

@protocol GSPenProtocol;

@interface GSShape : NSObject </*NSCoding, */ NSCopying, GSSelectableElementProtocol> {
#ifdef DEBUG_
	__unsafe_unretained GSContainer *_parent;
#else
	__weak GSContainer *_parent;
#endif
#pragma GSAutoCodeStart ivars

	NSMutableDictionary *_attributes;

#pragma GSAutoCodeEnd ivars

  @public
	BOOL _locked;
	NSPoint _position;
	NSPoint _positionPrecise;
}

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

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

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

- (BOOL)isEqualToShape:(GSShape *)other;

- (GSGlyph *)glyph;

/// The position of the element.
@property (assign, nonatomic) NSPoint position;

/** The unrounded position of the element.

 This is used for consecutive operations to avoid rounding errors
 */
@property (nonatomic, readonly) NSPoint positionPrecise;

/** Sets the position without notifying the path and no undo.

 This is mostly used to build a new element list that is later added to the layer. Otherwise you need to make sure to announce the changes. e.g. by calling `[GSLayer elementDidChange:]`.

 @param position The new position
 */

- (void)setPositionFast:(NSPoint)position;

- (void)setPositionFast:(NSPoint)position round:(CGFloat)grid;

- (void)transformFast:(NSAffineTransform *)transform round:(CGFloat)gridLength;

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

- (void)transform:(NSAffineTransform *)transform selection:(nullable NSOrderedSet *)selection;

- (void)getPositionsFromShape:(GSShape *)otherPath;

- (void)roundToGrid:(CGFloat)gridLength;

- (void)roundToGridFast:(CGFloat)gridLength;
/**
 Moves the path by the offset.

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

@property (nonatomic, getter=isLocked) BOOL locked;

/// For compatibility.
- (BOOL)locked;

- (void)makeUnlocked;

- (void)makeLocked;

- (void)elementDidChange:(id)element;

#ifndef GLYPHS_VIEWER
/// Returns the undoManager of the containing glyph.
@property (nonatomic, readonly, nullable) NSUndoManager *undoManager;

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

@property (readonly) GSShapeType shapeType;

- (void)scaleAttributes:(CGFloat)scale;

- (BOOL)getStrokeSettingsWidth:(nullable CGFloat *)width height:(CGFloat *)height pos:(nullable CGFloat *)pos capStart:(nullable GSLineCapStyle *)capStart capEnd:(nullable GSLineCapStyle *)capEnd join:(nullable NSLineJoinStyle *)join fill:(nullable BOOL *)fill mask:(nullable BOOL *)mask;
#ifndef GLYPHS_VIEWER
/**
 writes that shape to file

 @param file Nullable for testing purposes.
 @param formatVersion The file format version
 @param error error
 @return return value description
 */
- (BOOL)saveToFile:(FILE *)file format:(GSFormatVersion)formatVersion error:(NSError **)error;
#endif

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

 This is used to store the data in the .glyphs file.
 @param format the version of the dict
 */
- (nullable NSDictionary *)propertyListValueFormat:(GSFormatVersion)format;

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

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

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

 Calculates the bounds from the bare shapes, not removing outside overlaps and such.

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

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

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

- (void)drawInPen:(NSObject<GSPenProtocol> *)pen openPen:(nullable NSObject<GSPenProtocol> *)openPen secondaryPen:(nullable NSObject<GSPenProtocol> *)secondPen extraHandles:(nullable NSMutableArray *)extraHandles;

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

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

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

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

/**
 return value for key in tempData

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

- (NSInteger)countOfTempData;

#pragma GSAutoCodeStart methods

#pragma mark Attributes

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

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

- (void)setAttributes:(nullable NSDictionary *)attributes format:(GSFormatVersion)format;

/**
 convenience accessor to get to the content of the attributes dict

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

/** Adds something to the fonts attributes.

 This also triggers undo/document dirty state.

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

/** Removed the object with this key.

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

@property (readonly, nonatomic) NSUInteger countOfAttributes;

#pragma GSAutoCodeEnd methods

@end

NS_ASSUME_NONNULL_END
