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

@class GSFont;
@class GSGlyph;
@class GSLayer;
@class GSGuide;
@class GSCustomParameter;
@class GSAlignmentZone;
@class GSInfoProperty;
@class GSValueStore;
@class GSTTStem;
@class GSSaveContext;

#import <GlyphsCore/GSAxis.h>
#import <GlyphsCore/GSContainerProtocol.h>
#import <GlyphsCore/GSMetric.h>
#import <GlyphsCore/GSMetricStore.h>
#import <GlyphsCore/GSParametersAndPropertiesProtocol.h>
#import <GlyphsCore/GSUserDataProtocol.h>
#import <GlyphsCore/GSBase.h>

NS_ASSUME_NONNULL_BEGIN

typedef NS_ENUM(int8_t, GSMetricsAlignmentType) {
	GSMetricsAlignmentNone = -1,
	GSMetricsAlignmentOnMetrics = 1,
	GSMetricsAlignmentOnGuide = 2,
	GSMetricsAlignmentInZone = 3,
	GSMetricsAlignmentOnSidebearing = 4,
};

/** The class defining the fontMaster object */
#ifndef GLYPHS_VIEWER
@interface GSFontMaster : NSObject <NSCoding, NSCopying, GSContainerProtocol, GSParametersAndPropertiesProtocol, GSUserDataProtocol>
#else
@interface GSFontMaster : NSObject
#endif

@property (unsafe_unretained, nonatomic, nullable) GSFont *font;
/// @name Properties

/** the name of the FontMaster
e.g; "Regular", "Condensed", ...
*/

@property (nonatomic, strong) NSString *name;

@property (nonatomic, strong) NSString *iconName;

@property (nonatomic, readonly) NSImage *icon;

@property (nonatomic, readonly) NSString *nameUI;

@property (nonatomic, readonly) NSString *nameCoordinates;

/**
If it should be used for interpolations.
 */
@property (nonatomic) BOOL active;

@property (nonatomic, strong, nullable) NSDictionary *readBuffer;

@property (readonly) NSArray<GSMetricStore *> *axes;

@property (strong, nonatomic, nullable) GSAxisValues *axesValues;

/// The memory of the returned pointer must be freed by the caller.
- (nullable CGFloat *)axesValuesList;

#ifndef GLYPHS_LITE
- (nullable GSMetricStore *)axisValueForId:(NSString *)ID;

- (void)setAxisValue:(nullable GSMetricStore *)value forId:(NSString *)ID;

/// Returns `0` if no value exists for the given id.
- (CGFloat)axisInternalValueValueForId:(NSString *)ID;

- (void)setAxisInternalValueValue:(CGFloat)value forId:(NSString *)ID;

- (CGFloat)axisValueValueForId:(NSString *)ID GSDeprecated("axisInternalValueValueForId:");

- (void)setAxisValueValue:(CGFloat)value forId:(NSString *)ID GSDeprecated("setAxisInternalValueValue:forId:");

/// Returns `0` if no value exists for the given id.
- (CGFloat)axisExternalValueValueForId:(NSString *)ID;

- (void)setAxisExternalValueValue:(CGFloat)value forId:(NSString *)ID;

#endif

/// The id
@property (copy, nonatomic) NSString *id;

@property (nonatomic) BOOL visible;

/// this is for hiding it from profiles
@property (nonatomic) BOOL hidden;

#ifndef GLYPHS_VIEWER
/** Delays redrawing and updates.

 Will record the changes and update once in `enableUpdateInterface`. If you don’t need updates, use `stopUpdateInterface`

 @see stopUpdateInterface
 @warning Always call enableUpdateInterface afterwards
 */
- (void)disableUpdateInterface;

/** Disable redrawing and updates completely.

 Changes will not trigger an update. Only use this carefully and call enableUpdateInterface if you need future updates.

 @see disableUpdateInterface
 @warning Always call enableUpdateInterface afterwards
 */
- (void)stopUpdateInterface;

/** Reenables redrawing and updates.

 This only has an effect if disableUpdateInterface was called before
 */
- (void)enableUpdateInterface;

- (void)enableFutureUpdates;
/** Check if the updating is enabled

 @return NO if disableUpdateInterface was called
 */
- (BOOL)isUpdateInterfaceEnabled;
#endif

#pragma mark - Metrics

- (void)addDefaultMetrics;

- (void)setDefaultMetric:(CGFloat)value forType:(GSMetricsType)type;

- (void)setDefaultMetric:(CGFloat)position overshoot:(CGFloat)over forType:(GSMetricsType)type;

- (nullable GSMetricStore *)metricValueForType:(GSMetricsType)type layer:(GSLayer *)layer;

- (nullable GSMetricStore *)defaultMetricValueForType:(GSMetricsType)type;

- (nullable GSMetricStore *)metricValueAtPosition:(CGFloat)position;

- (CGFloat)defaultMetricForType:(GSMetricsType)type;

- (CGFloat)metricForType:(GSMetricsType)type layer:(nullable GSLayer *)layer;

- (CGFloat)metricForType:(GSMetricsType)type script:(NSString *)script;
/** The default ascender.
 This is kept for compatibility, for now.
 */
@property (nonatomic, readonly) CGFloat defaultAscender;

- (CGFloat)ascenderForLayer:(GSLayer *)layer;

@property (nonatomic, readonly) CGFloat defaultDescender;

- (CGFloat)descenderForLayer:(GSLayer *)layer;

/// The capHeight.
@property (nonatomic, readonly) CGFloat defaultCapHeight;

//- (CGFloat)capHeightForLayer:(GSLayer *)layer;
/// The xHeight.
@property (nonatomic, readonly) CGFloat defaultXHeight;

- (CGFloat)xHeightForLayer:(GSLayer *)layer;

- (CGFloat)slantHeightForLayer:(nullable GSLayer *)layer;

- (CGFloat)topHeightForLayer:(GSLayer *)layer;

#pragma mark metricValues

@property (nonatomic, strong) NSDictionary<NSString *, GSMetricStore *> *metricValues;

- (GSMetricStore *)valueForMetric:(GSMetric *)metric;

- (void)setMetricValue:(nullable GSMetricStore *)object forId:(NSString *)ID;

- (void)setMetricValue:(nullable GSMetricStore *)object forId:(NSString *)ID notify:(BOOL)notify;

- (GSMetricStore *)setMetricPosition:(CGFloat)pos overshoot:(CGFloat)over type:(GSMetricsType)type name:(nullable GSMetricsKey)name filter:(nullable NSString *)filter;

- (NSArray<GSMetricStore *> *)metrics;

- (NSArray<GSMetricStore *> *)defaultMetrics;

#pragma mark StemValues

@property (nonatomic, strong) NSDictionary<NSString *, GSValueStore *> *stemValues;

#ifndef GLYPHS_VIEWER
- (GSValueStore *)valueForStem:(GSMetric *)stem;

- (CGFloat)valueValueForStemId:(NSString *)stemId;

- (void)setStemValue:(nullable GSValueStore *)object forId:(NSString *)ID;

- (void)setStemValueValue:(CGFloat)value forId:(NSString *)ID;

- (NSArray<GSValueStore *> *)stems;

- (NSArray<GSValueStore *> *)defaultStems;

- (NSArray<NSNumber *> *)stemValuesArray;

#ifndef GLYPHS_LITE
/// convenient for TT export
- (NSArray<GSTTStem *> *)ttStems;
#endif
#endif

#pragma mark NumberValues

@property (nonatomic, strong, nullable) NSDictionary<NSString *, GSValueStore *> *numberValues;

- (GSValueStore *)valueForNumber:(GSMetric *)number;

- (GSValueStore *)numberValueForId:(NSString *)ID;

- (GSValueStore *)numberValueForName:(NSString *)name;

- (CGFloat)numberValueValueForId:(NSString *)ID;

- (CGFloat)numberValueValueForName:(NSString *)name;

#ifndef GLYPHS_LITE
- (CGFloat)numberValueValueForToken:(NSString *)token;
#endif

#ifndef GLYPHS_VIEWER
- (void)setNumberValue:(GSValueStore *)object forId:(NSString *)ID;

- (void)setNumberValue:(GSValueStore *)object forName:(NSString *)name;

- (void)setNumberValueValue:(CGFloat)floatValue forId:(NSString *)ID;

- (void)setNumberValueValue:(CGFloat)floatValue forName:(NSString *)name;

- (NSArray<GSValueStore *> *)numbers;

- (NSArray<NSNumber *> *)numberValuesArray;
#endif

#pragma mark -

/** The Italic angle
 */
@property (nonatomic) CGFloat defaultItalicAngle;

- (CGFloat)italicAngleForLayer:(GSLayer *)layer;

///// list of alignment zones
//@property (readonly, nonatomic) NSArray *alignmentZones;

- (instancetype)initFast;

//@property (strong, nonatomic) NSMutableArray *customParameters;

/// @name Initializing an Font Master

/**
 Inits with a dict.

 @param dict The property list representation
 @param formatVersion The file format version
 @return a fontmaster object
 */
- (instancetype)initWithDict:(NSDictionary *)dict format:(GSFormatVersion)formatVersion;

- (void)loadReadBuffer;

#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
 */
- (nullable NSDictionary *)propertyListValueFormat:(GSFormatVersion)format error:(out NSError **)error;

- (BOOL)saveToFile:(FILE *)file format:(GSFormatVersion)formatVersion context:(GSSaveContext *)context error:(out NSError **)error;

- (nullable NSArray<NSNumber *> *)blueValuesError:(out NSError **)error;

- (nullable NSArray<NSNumber *> *)blueValuesMetrics:(NSArray<GSMetric *> *)metricStores doFallback:(BOOL)doFallback error:(out NSError **)error;

- (nullable NSArray<NSNumber *> *)otherBluesError:(out NSError **)error;

- (nullable NSArray<NSNumber *> *)otherBluesMetrics:(NSArray<GSMetric *> *)metricStores doFallback:(BOOL)doFallback error:(out NSError **)error;

- (NSArray *)alignmentZonesForLayer:(GSLayer *)layer;

+ (GSMetricsAlignmentType)isOnMetrics:(NSPoint)position metrics:(NSArray<GSMetricStore *> *)metrics italicAngle:(CGFloat)italicAngle inLayer:(GSLayer *)layer;

- (BOOL)isOnMetricsLine:(NSPoint)position layer:(GSLayer *)layer;
#endif

@property (nonatomic, assign) uint32_t gridMainHorizontal;

@property (nonatomic, assign) uint32_t gridMainHorizontalFast;

@property (nonatomic, assign) uint32_t gridMainVertical;

@property (nonatomic, assign) uint32_t gridMainVerticalFast;

@property (nonatomic, assign) uint32_t gridSubDivisionHorizontal;

@property (nonatomic, assign) uint32_t gridSubDivisionVertical;

@property (nonatomic, assign) int32_t gridSlopeRise;

@property (nonatomic, assign) int32_t gridSlopeRun;

@property (assign) uint32_t gridMainHorizontalStore;

@property (assign) uint32_t gridMainVerticalStore;

@property (assign) uint32_t gridSubDivisionHorizontalStore;

@property (assign) uint32_t gridSubDivisionVerticalStore;

@property (assign) int32_t gridSlopeRiseStore;

@property (assign) int32_t gridSlopeRunStore;

- (GSGridSettings)roundingSettings;

- (GSGridSettings)roundingSettingsMax:(CGFloat)maxLength;

- (CGFloat)gridLengthHorizontal;

- (CGFloat)gridLengthVertical;

#ifndef GLYPHS_VIEWER
- (NSPoint)alignPointToGrid:(NSPoint)point type:(GSNodeType)nodeType;

+ (NSPoint)alignPoint:(NSPoint)point toGrid:(GSGridSettings)grid type:(GSNodeType)type;

#ifndef GLYPHS_LITE

+ (CGFloat)localizedParameterValue:(NSString *)parameter glyph:(GSGlyph *)glyph;

+ (nullable NSString *)localizedParameter:(NSString *)parameter glyph:(GSGlyph *)glyph;

- (CGFloat)localizedCustomValueForKey:(NSString *)key layer:(GSLayer *)layer;

- (CGFloat)localizedCustomValueForKey:(NSString *)key script:(NSString *)script;

/// @name Other Methods
/** Scales the Font Master
Scales the vertical metrics, alignment zones and guide lines
@param scale the scale factor (1 means no scaling.)
*/
- (void)scaleBy:(CGFloat)scale;

/**
 Adds a new font info value to the array specified by `key` or updates an existing one.

 @param name The key whose plural form specifies an array property of `GSInfoValue` objects on the receiver.
 @param value The actual value to be added. Or nil to remove an existing value
 @param languageTag The language under which the value should be stored. Defaults to `English` if nil is passed.
 */
- (void)setProperty:(GSPropertyName)name value:(nullable NSString *)value languageTag:(nullable NSString *)languageTag;

- (nullable GSInfoProperty *)propertyForName:(GSPropertyName)name;

- (nullable NSString *)defaultPropertyForName:(GSPropertyName)name;

- (void)setDefaultProperty:(GSPropertyName)name value:(id)value;

+ (NSArray *)iconNames;

- (NSString *)defaultIconName;

/// splits the name into values that can be stored in v1 files
- (void)legacyNames:(NSString *)name weight:(NSString *_Nonnull*_Nullable)weight width:(NSString *_Nonnull*_Nullable)width custom:(NSString *_Nonnull*_Nullable)custom;

- (void)invalidateCustomParameterCache;
#endif
#endif

- (void)setReadBuffer:(NSObject *)values forKey:(NSString *)key;

#pragma mark - start auto code

#pragma GSAutoCodeStart methods

#pragma mark Guides

/// The guides.
@property (nonatomic, strong, null_resettable) NSMutableArray<GSGuide *> *guides;

#ifndef GLYPHS_VIEWER
/** The number of guides */
- (NSUInteger)countOfGuides;

/** Returns object at idx in guides

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

/** Returns the index of guide in guides

 @param guide the object
 @return the index of the object
 */
- (NSUInteger)indexOfObjectInGuides:(GSGuide *)guide;

/** Adds the guide

 @param guide the object to be added
 */
- (void)addGuide:(GSGuide *)guide;

/** Inserts the guide at index into guides

 @param guide The object to insert
 @param idx The index
 */
- (void)insertObject:(GSGuide *)guide inGuidesAtIndex:(NSUInteger)idx;

/** Removes the guide

 @param guide the object to be removed
 */
- (void)removeObjectFromGuides:(GSGuide *)guide;

/** Removes the guide at idx

 @param idx The index
 */
- (void)removeObjectFromGuidesAtIndex:(NSUInteger)idx;
#endif

#pragma mark CustomParameters

/// @name customParameters

/**
 The customParameters.
 A list of customParameters objects
 */
@property (nonatomic, strong, null_resettable) NSMutableArray<GSCustomParameter *> *customParameters;

/** The count of customParameters */
- (NSUInteger)countOfCustomParameters;

- (nullable GSCustomParameter *)objectInCustomParametersAtIndex:(NSUInteger)idx;

- (BOOL)customBoolValueForKey:(NSString *)key;

- (nullable id)customColorValueForKey:(NSString *)key;

/// The value of the customParameters where name == Key
- (nullable id)customValueForKey:(NSString *)key;

/// The customParameters where name == Key
- (nullable GSCustomParameter *)customParameterForKey:(NSString *)key;

- (nullable GSCustomParameter *)customParameterActiveForKey:(NSString *)key;

/** Set the value for key

 It will look for an existing parameter with the name an overwrite its value, or adds a new parameter
 @param value The value to add
 @param key   The name of the parameter
 */
- (void)setCustomValue:(nullable id)value forKey:(NSString *)key;

#ifndef GLYPHS_VIEWER
- (void)addCustomParameter:(GSCustomParameter *)property;

- (void)removeObjectFromCustomParameters:(GSCustomParameter *)customParameter;

/** Removes the first parameter with then Name

 @param key The name
 */
- (void)removeObjectFromCustomParametersForKey:(NSString *)key;

- (void)insertObject:(GSCustomParameter *)customParameter inCustomParametersAtIndex:(NSUInteger)idx;

- (void)removeObjectFromCustomParametersAtIndex:(NSUInteger)idx;

- (void)replaceObjectInCustomParametersAtIndex:(NSUInteger)idx withObject:(GSCustomParameter *)customParameter;
#endif

#pragma mark Properties
/// @name Properties

/** The properties.
 this contains (localizable) names and other font info settings
 */
@property (nonatomic, strong, null_resettable) NSMutableArray<GSInfoProperty *> *properties;

#ifndef GLYPHS_VIEWER
/** The number of properties */
- (NSUInteger)countOfProperties;

/** Returns object at idx in properties

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

/** Returns the index of property in properties

 @param property the object
 @return the index of the object
 */
- (NSUInteger)indexOfObjectInProperties:(GSInfoProperty *)property;

/** Adds the property

 @param property the object to be added
 */
- (void)addProperty:(GSInfoProperty *)property;

/** Inserts the property at index into properties

 @param property The object to insert
 @param idx The index
 */
- (void)insertObject:(GSInfoProperty *)property inPropertiesAtIndex:(NSUInteger)idx;

/** Removes the property

 @param property the object to be removed
 */
- (void)removeObjectFromProperties:(GSInfoProperty *)property;

/** Removes the property at idx

 @param idx The index
 */
- (void)removeObjectFromPropertiesAtIndex:(NSUInteger)idx;
#endif

#pragma mark UserData

/// @name UserData
/** 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 *userData;

#ifndef GLYPHS_VIEWER
/**
 convenience accessor to get to the content of the userData dict

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

/** Adds something to the fonts userData.

 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)setUserData:(nullable id)value forKey:(NSString *)key;

/** Removed the object with this key.

 @param key The key to remove
 */
- (void)removeUserDataForKey:(NSString *)key;
#endif

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

- (NSUInteger)countOfTempData;

/**
 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:(NSString *)key;

/**
 return value for key in tempData

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

#pragma GSAutoCodeEnd methods

@end
NS_ASSUME_NONNULL_END
