//
//  GSGlyph.h
//  FontBezier Doc
//
//  Created by Georg Seifert on 20.10.05.
//  Copyright 2005 schriftgestaltung.de. All rights reserved.
//

#import <Cocoa/Cocoa.h>
#import <GlyphsCore/GSFont.h>
#import <GlyphsCore/GSUndoManager.h>
#import <GlyphsCore/GSUserDataProtocol.h>

@class GSFont;
@class GSLayer;
@class MGOrderedDictionary;
@class GSInstance;
@class GSPartProperty;
@class GSGlyphInfo;

typedef NS_ENUM(NSInteger, GSGlyphCopyOptions) {
	GSGlyphCopyNoLayers = 0,
	GSGlyphCopySpecialLayers = 1 << 0,
	GSGlyphCopyAllLayers = 2 << 0,
};

#define OTFErrorGenerateFont 528
#define OTFErrorAddInflections 529
#define OTFErrorGlyphsWithError 530
/**
 The class defining the glyph object
 */

NS_ASSUME_NONNULL_BEGIN

@interface GSGlyph : NSObject <NSCoding, NSCopying, GSUserDataProtocol, GSUndoClientProtocol> {
	MGOrderedDictionary *_layers;
	NSString *_name;
	NSMutableOrderedSet *_unicodes;
	NSString *_script;
	NSString *_note;
	NSString *_category;
	NSString *_subCategory;
	GSCase _case;
	GSWritingDirection _direction;
	NSString *_leftMetricsKey;
	NSString *_widthMetricsKey;
	NSString *_rightMetricsKey;
	NSString *_vertWidthMetricsKey;
	NSString *_vertOriginMetricsKey;
	NSString *_topMetricsKey;
	NSString *_bottomMetricsKey;

	BOOL _export;
	NSColor *_color;
	GSLabelColors _colorIndex;
	NSMutableDictionary *_userData;
	NSDate *_lastChange;
	NSInteger _changeCount;

	NSString *_production;
	BOOL _storeScript;
	BOOL _storeCategory;
	BOOL _storeSubCategory;
	BOOL _storeCase;
	BOOL _storeProduction;
	BOOL _storeDirection;
	NSString *_sortName;
	NSString *_sortNameKeep;
	BOOL _storeSortName;

	BOOL _locked;
	BOOL _justLocked;
	NSMutableOrderedSet<NSString *> *_tags;
#ifdef GLYPHS_VIEWER
	unsigned char *_fileStart;
	unsigned char *_fileEnd;
  @public
	GSPlistParseInfo _pInfoStore;
	GSPlistParseInfo *_pInfo;
#endif

#pragma GSAutoCodeStart ivars

#pragma GSAutoCodeEnd ivars
}

- (instancetype)initFast;

- (void)lockReadGlyph;
- (void)unlockReadGlyph;
- (void)lockWriteGlyph;
- (void)unlockWriteGlyph;
- (void)lockReadAllLayersShapes;
- (void)unlockReadAllLayersShapes;

- (BOOL)isEqualToGlyph:(GSGlyph *)other;
/// @name Properties

/** A pointer to the containing GSFont object */
@property (weak, nonatomic, nullable) GSFont *parent;
@property (readonly, nonatomic, nullable) GSFont *font;
@property (assign, nonatomic) NSInteger changeCount;

/// The glyphs name
@property (nonatomic, copy, nullable) NSString *name;
/// The unicode value of the glyph. This is normally set automatically on setting the name
@property (nonatomic, copy, nullable) NSString *unicode;

@property (strong, nonatomic, nullable) NSOrderedSet *unicodes;

- (void)setUnicodesFast:(NSOrderedSet *)unicodes;

- (void)addUnicode:(NSString *)unicode;

- (void)removeUnicode:(NSString *)unicode;

- (NSUInteger)countOfUnicodes;

/** A character code of the glyph of 0 if it has no unicode */
@property (nonatomic, readonly) UTF32Char unicodeChar;

/// The glyphs note
@property (nonatomic, copy, nullable) NSString *note;

/** The language/script of the glyph.

 e.g: latin, cyrillic, greek
 This is set from the internal database.
 */
@property (nonatomic, copy, nullable) GSScriptID script;
@property (nonatomic) BOOL storeScript;

@property (nonatomic) BOOL isCJK;

/** The category of the glyph

 e.g: letter, number, punctuation ...
 this is set from the internal database.
 */
@property (nonatomic, copy, nullable) NSString *category;
@property (nonatomic) BOOL storeCategory;

/** The sub category of the glyph

 e.g: lower case, smallcaps ...
 this is set from the internal database.
 */
@property (nonatomic, copy, nullable) NSString *subCategory;
@property (nonatomic) BOOL storeSubCategory;

- (GSCase)case;
- (void)setCase:(GSCase)aCase;
- (void)setCaseFast:(GSCase)aCase;
@property (nonatomic) BOOL storeCase;

/** the production name
 */
@property (nonatomic) BOOL storeProduction;
@property (strong, nonatomic, nullable) NSString *production;

/** the writing direction
 */
@property (nonatomic) BOOL storeDirection;
@property (nonatomic) GSWritingDirection direction;

/// The string the glyph is composed of.
@property (nonatomic, strong, nullable) NSString *baseString;

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

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

- (NSDictionary *)propertyListValueFormat:(GSFormatVersion)format options:(int)options;
#endif

/// The left metrics key
@property (copy, nonatomic, nullable) NSString *leftMetricsKey;
/// The width metrics key
@property (copy, nonatomic, nullable) NSString *widthMetricsKey;
/// The right metrics key
@property (copy, nonatomic, nullable) NSString *rightMetricsKey;
/// The top metrics key
@property (copy, nonatomic, nullable) NSString *topMetricsKey;
/// The vertical origin metrics key
@property (copy, nonatomic, nullable) NSString *vertOriginMetricsKey;
/// The vertical width metrics key
@property (copy, nonatomic, nullable) NSString *vertWidthMetricsKey;
/// The bottom metrics key
@property (copy, nonatomic, nullable) NSString *bottomMetricsKey;

- (nullable NSString *)previousKerningGroupForDirection:(GSWritingDirection)direction;
- (void)setPreviousKerningGroup:(NSString *)group forDirection:(GSWritingDirection)direction;
- (nullable NSString *)previousKerningGroupIDforDirection:(GSWritingDirection)direction;
- (void)setPreviousKerningGroupID:(NSString *)groupId forDirection:(GSWritingDirection)direction;
- (nullable NSString *)nextKerningGroupForDirection:(GSWritingDirection)direction;
- (void)setNextKerningGroup:(NSString *)group forDirection:(GSWritingDirection)direction;
- (nullable NSString *)nextKerningGroupIDforDirection:(GSWritingDirection)direction;
- (void)setNextKerningGroupID:(NSString *)groupId forDirection:(GSWritingDirection)direction;

/// The name of the kerning group for left side kerning.
@property (copy, nonatomic, nullable) NSString *leftKerningGroup;
/// The name of the kerning group for right side kerning.
@property (copy, nonatomic, nullable) NSString *rightKerningGroup;

/** The id of the key glyph for left side kerning.

 this should look like this: `@MM_L_Name`
 */
@property (copy, nonatomic, nullable) NSString *leftKerningGroupId;

/** The id of the key glyph for right side kerning.

 this should look like this: `@MM_R_Name`
 */
@property (copy, nonatomic, nullable) NSString *rightKerningGroupId;

@property (copy, nonatomic, nullable) NSString *topKerningGroup;

@property (copy, nonatomic, nullable) NSString *bottomKerningGroup;

/** The id of the key glyph for top kerning.

 this should look like this: `@MM_T_Name`
 */
@property (copy, nonatomic, nullable) NSString *topKerningGroupId;

/** The id of the key glyph for bottom kerning.

 this should look like this: `@MM_B_Name`
 */
@property (copy, nonatomic, nullable) NSString *bottomKerningGroupId;

#ifndef GLYPHS_VIEWER
- (BOOL)validateKerningGroup:(id __nullable *__nullable)ioValue error:(NSError **)outError;

- (BOOL)validateMetricsKey:(id __nullable *__nullable)ioValue error:(NSError **)outError;
#endif

#ifndef __cplusplus // RMX complains about that.

/// If the glyph will end up in the final font
@property (nonatomic) BOOL export;
#endif

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

/** Returns the index of the color label

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

#ifndef GLYPHS_VIEWER
/** The glyphs undoManager

 Each GSGlyph object has its own undoManager

 @see [GSFont.undoManager]([GSFont undoManager])

 @return a NSUndoManager object
 */
@property (strong, nonatomic, nullable) NSUndoManager *undoManager;

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

#endif
/// The current index of the glyph
@property (nonatomic) NSUInteger glyphId;

/** The glyph key is used to access the glyph.

 This is used to keep track of the glyph even if is renamed.
 It is used for the kerning.
 It could look like this: `FBCA074D-FCF3-427E-A700-7E318A949AE5`
 */
@property (strong, nonatomic) NSString *id;
/**
 The names is used for sorting.

 If set it will be used for sorting inside of
 */
@property (strong, nonatomic, nullable) NSString *sortName;
@property (strong, nonatomic, nullable) NSString *sortNameKeep;
@property (nonatomic) BOOL storeSortName;

- (void)setLockedFast:(BOOL)locked;

- (void)initLock;

// for compatibility with plugins
@property (nonatomic, strong, nullable) NSDate *lastOperation;

/** Defines the parameters for Smart Glyphs.

 It contains an array of GSPartProperty objects.
 */
@property (strong, nonatomic, nullable) NSMutableArray<GSPartProperty *> *partsSettings;

#ifndef GLYPHS_LITE
- (NSUInteger)countOfPartsSettings;
- (nullable GSPartProperty *)objectInPartsSettingsAtIndex:(NSUInteger)idx;
#ifndef QUICKLOOK
- (void)insertObject:(GSPartProperty *)partProperty inPartsSettingsAtIndex:(NSUInteger)idx;
//- (void)insertPartsSettings:(NSArray *)_partsSettingArray atIndexes:(NSIndexSet *)indexes;
- (void)removeObjectFromPartsSettingsAtIndex:(NSUInteger)idx;
//- (void)removePartsSettingsAtIndexes:(NSIndexSet *)indexes;
- (void)replaceObjectInPartsSettingsAtIndex:(NSUInteger)idx withObject:(GSPartProperty *)partProperty;
#endif
#endif

- (NSString *)charString;

//@property(retain) id itemView;
/// InItialization
/// Initializes a newly allocated glyph with this name
//- (id) initWithName:(NSString*) Name ;

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

 @param glyphDict A dictionary
 @param formatVersion The file format version
 */
- (instancetype)initWithDict:(NSDictionary *)glyphDict format:(GSFormatVersion)formatVersion;

#ifndef GLYPHS_VIEWER

// FIXME: formatVersion is ultimately set to GSFont.formatVersion, which is a CGFloat!
/**
 @discussion saves the object to file
 @param file		a File
 @param formatVersion The file format version
 @param options save options (e.g. autosave, disable lastChange
 @param error	on return an error containing the reason for failure
 @return YES if successful
 */

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

#endif
/// @name Methods

#ifndef GLYPHS_VIEWER
#ifndef LIBCORE
- (nullable NSError *)validateName:(NSString *)name;

- (nullable NSString *)ensureValidName:(NSString *)name;

+ (BOOL)isValidNameString:(NSString *)name allowSpaces:(BOOL)allowSpaces error:(NSError **)error;

+ (BOOL)isValidUnicodeString:(NSString *)unicode error:(NSError **)error;
#endif
#endif

- (void)setNameFast:(NSString *)name;

- (void)setNameFastCache:(NSString *)name;

- (void)setName:(NSString *)name changeName:(BOOL)changeName;

- (void)setName:(NSString *)name changeName:(BOOL)changeName update:(BOOL)update validate:(BOOL)validate;

#pragma Layers

/// @name Layers

/** An array of GSLayer objects.

 It should have at least the same count as font.fontMasters.

 The keys are the id of the font masters and should be the same as the [GSLayer.associatedMasterId]([GSLayer associatedMasterId]) if the layer that is associated by the key. For 'master layers', it should also be equal to the [GSLayer.layerId]([GSLayer layerId]).
 */
@property (strong, nonatomic, nonnull) NSDictionary<NSString *, GSLayer *> *layers;

/** The number of layers in the glyph

 @return The number of layers
 */
- (NSUInteger)countOfLayers;


/** Returns the a layer located at an Index.

 @param key The Id of the layer
 @return A GSLayer object or nil if not found
 */
- (nullable GSLayer *)layerForId:(nonnull NSString *)key;

#ifndef GLYPHS_VIEWER
- (nullable GSLayer *)layerForIdFast:(nonnull NSString *)key;

- (nullable GSLayer *)layerForName:(nonnull NSString *)key;

- (nullable GSLayer *)layerForName:(nonnull NSString *)name masterId:(nonnull NSString *)masterID;

- (nullable NSArray *)layersForName:(nullable NSString *)name masterIDs:(NSArray *)masterIDs;
#endif

- (nullable GSLayer *)layerForKey:(NSString *)key masterId:(nullable NSString *)masterID;

#ifndef GLYPHS_VIEWER
- (nullable GSLayer *)layerNearestToPosition:(CGFloat *)position associatedMasterId:(nullable NSString *)associatedMasterId onlyBraceLayer:(BOOL)onlyBraceLayer tolerance:(CGFloat)to;
#endif

- (GSLayer *)objectInLayersAtIndex:(NSUInteger)idx;

/**
 Adds the Layer with the key.
 @discussion If the Key is a id of a GSFontMaster, it will be a master layer, otherwise the associated master ID of the layer should be set to a ID of a master.
 @param layer The GSLayer object.
 @param key The layer key. For master layers this is the id of the corresponding GSFontMaster.
 */
- (void)setLayer:(GSLayer *)layer forId:(id)key;

- (NSArray<GSLayer *> *)sortedLayers;

/**
 Removes the Layer for the key.

 @param key The layer key.
 */
- (void)removeLayerForId:(id)key;

#ifndef GLYPHS_LITE

- (void)resetMasterCompatibilityCheckStatus;
/**
 Returns if the glyphs has compatible outlines.

 @return YES if the layers have compatible outlines.
 */
- (BOOL)mastersCompatible;

/**
 Returns if the glyphs has compatible outlines in the layers for all FontMasterIds

 @param fontMasterIds the Master IDs
 @return YES if the layers have compatible outlines.
 */
- (BOOL)mastersCompatibleForLayerIds:(NSArray *)fontMasterIds;

- (BOOL)mastersCompatibleForLayers:(NSArray<GSLayer *> *)layers;

#ifndef GLYPHS_VIEWER
- (void)sortLayers:(NSMutableArray *)layers;
#endif

- (NSArray <NSOrderedSet *> *)layerGroups;

- (NSArray <NSOrderedSet *> *)layerGroups:(nullable NSArray *)instances masters:(nullable NSArray *)masters error:(NSError **)error;

- (NSMutableArray <NSMutableOrderedSet *> *)forcedLayerGroupIdsSeenLayers:(nullable NSMutableSet *)seenLayers;

- (nullable NSArray <GSFontMaster *> *)collectBraceLayerIDsMasters:(NSArray *)masters allKeys:(NSOrderedSet *)allKeys;

#ifndef GLYPHS_VIEWER
- (void)resetLayerKeys;
#endif

// TODO: this is doing way less than the interpolate:masters:… method below. Rename or refactor this to make it clear.
- (nullable GSLayer *)interpolate:(NSDictionary *)interpolation scale:(CGFloat)scale error:(NSError **)error;

- (nullable GSLayer *)interpolate:(nonnull NSDictionary *)interpolation scale:(CGFloat)scale masters:(nonnull NSArray *)masters decompose:(BOOL)decompose error:(NSError **)error;

/**
 Generates an interpolated layer

 @param instance  The instance that defines the interpolation values
 @param masters   The masters to use for the interpolation
 @param keepSmart If smart components should be decomposed before interpolation.
 @param error if there is a problem, it returns an NSError object (by reference) that contains details.
 @return The interpolated layer, or nil if something went wrong.
 */

- (nullable GSLayer *)interpolate:(nullable GSInstance *)instance scale:(CGFloat)scale masters:(NSArray *)masters keepSmart:(BOOL)keepSmart smartSettings:(nullable NSDictionary *)smartSettings layerName:(nullable NSString *)layerName additionalLayers:(nullable NSMutableArray *)additionalLayers error:( NSError * _Nullable *)error;

- (void)interpolateSmartLayer:(GSLayer *)baseLayer instance:(GSInstance *)instance scale:(CGFloat)scale targetGlyph:(GSGlyph *)targetGlyph;

- (void)ensureHintsInLayer:(GSLayer *)layer layers:(NSArray *)layers masters:(NSArray *)masters font:(GSFont *)font;

/**
 Replaces the content of the layers with an interpolation from the remaining layers.

 This is useful for 'brace' layers but also for a default state for middle masters.
 @param layers The layers that should be recalculated
 */
- (void)replaceLayersWithInterpolation:(NSArray <GSLayer *> *)layers;

- (void)transferHintsFromLayer:(GSLayer *)source to:(GSLayer *)target;

- (BOOL)decomposeSpecialComponents:(BOOL)keepSmart;

- (nullable NSString *)nextLayerId:(CGFloat *)layerAxisLocation layerPos:(CGFloat *)masterPos onAxis:(NSInteger)axisIdx direction:(int)direction;

#endif
/// Is called from other object after changing some of the glyphs properties.
- (void)outlineHasChanges;

- (id)copyThin:(BOOL)thin options:(GSGlyphCopyOptions)options;

/// If the glyph has any color layers
- (BOOL)isAnyColorGlyph;

- (BOOL)isFullColorGlyph;

/// If the glyph has COLR/CPAL layers
- (BOOL)isColorPaletteGlyph;

/// If the glyph has SVG layers
- (BOOL)isSVGColorGlyph;

/// If the glyph has Apple color glyph information
- (BOOL)isAppleColorGlyph;

- (BOOL)hasComponents;
/**
 Helper method to check it the name is a corner glyph

 @param name the name to check
 */
+ (BOOL)isCornerName:(NSString *)name;

/// It is a corner (cap, stroke, ...)
- (BOOL)isCornerGlyph;

#ifndef GLYPHS_LITE
/// It is a smart glyph
- (BOOL)isSmartGlyph;

/**
 similar to `isSmartGlyph` but without check `_partsSettings`.
 */
- (BOOL)realSmartGlyph;

/// It is an icon
- (BOOL)isIcon;

/// it is a SF-symbol
- (BOOL)isSFSymbol;

/// It has manually set info (see: storeScript ...)
- (BOOL)hasCustomGlyphInfo;

/// If the glyph has any attributes or smart settings
- (BOOL)hasSpecialLayers;

/// If the glyph has bracket layers
- (BOOL)hasBracketLayer;

/// If the glyph has bracket layers
- (BOOL)hasBraceLayer;

+ (nullable NSString *)modelGlyphForGlyph:(UTF32Char)unicode groups:(NSDictionary *)groups position:(NSString *)groupKey;
#endif

#ifndef GLYPHS_VIEWER
#pragma mark Tags

/// The tags.
@property (nonatomic, copy) NSOrderedSet<NSString *> *tags;

/** The number of tags */
- (NSUInteger)countOfTags;

/** Returns object at index in tags

 @param idx The index
 @return the object at index
 */
- (nullable NSString *)objectInTagsAtIndex:(NSUInteger)idx;

/** Returns the index of tag in tags

 @param tag the object
 @return the index of the object
 */
- (NSUInteger)indexOfObjectInTags:(NSString *)tag;

/** Adds the tag

 @param tag the object to be added
 */
- (void)addTag:(NSString *)tag;

///** Inserts the tag at index into tags
//
// @param tag The object to insert
// @param index The index
// */
//- (void)insertObject:(NSString *)tag inTagsAtIndex:(NSUInteger)idx;

/** Removes the tag

 @param tag the object to be removed
 */
- (void)removeObjectFromTags:(NSString *)tag;

/** Removes the tag at index

 @param idx The index
 */
- (void)removeObjectFromTagsAtIndex:(NSUInteger)idx;

/**
 helper to make bindings simpler
 */
@property (nonatomic, copy) NSArray<NSString *> *tagsArray;
#endif

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

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

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

/**
 return value for key in tempData

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

#pragma GSAutoCodeStart methods

#pragma GSAutoCodeEnd methods

@end

NS_ASSUME_NONNULL_END
