//
//  GSAxis.h
//  GlyphsCore
//
//  Created by Martin Winter on 11.05.18.
//  Copyright © 2018 schriftgestaltung.de. All rights reserved.
//

#import <GlyphsCore/GSBase.h>
#import <GlyphsCore/GSContainerProtocol.h>
#import <GSFontTools/GSFontTools.h>
#ifndef GLYPHS_LITE
#ifndef LIBCORE
#import <GlyphsCore/GSIdentifiableParameter.h>
#endif
#endif

@class GSFont;
@class GSMetricStore;
@class GSInfoValue;
@class GSSaveContext;

NS_ASSUME_NONNULL_BEGIN

/// Maps axis ids to metric values.
typedef NSDictionary<NSString *, GSMetricStore *> GSAxisValues;
typedef NSMutableDictionary<NSString *, GSMetricStore *> GSMutableAxisValues;

// for compatibly with some custom plugins. Remove when G4 is released
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
typedef NSString *GSAxisTag NS_SWIFT_NAME(GSAxis.Tag) NS_TYPED_EXTENSIBLE_ENUM GSDeprecated("OTAxisTag");

extern GSAxisTag const GSAxisTagEngr GSDeprecated("OTAxisTagEngr");
extern GSAxisTag const GSAxisTagHght GSDeprecated("OTAxisTagHght");
extern GSAxisTag const GSAxisTagItal GSDeprecated("OTAxisTagItal");
extern GSAxisTag const GSAxisTagOpsz GSDeprecated("OTAxisTagOpsz");
extern GSAxisTag const GSAxisTagOtln GSDeprecated("OTAxisTagOtln");
extern GSAxisTag const GSAxisTagRefl GSDeprecated("OTAxisTagRefl");
extern GSAxisTag const GSAxisTagRsec GSDeprecated("OTAxisTagRsec");
extern GSAxisTag const GSAxisTagRxad GSDeprecated("OTAxisTagRxad");
extern GSAxisTag const GSAxisTagRyad GSDeprecated("OTAxisTagRyad");
extern GSAxisTag const GSAxisTagShdw GSDeprecated("OTAxisTagShdw");
extern GSAxisTag const GSAxisTagSlnt GSDeprecated("OTAxisTagSlnt");
extern GSAxisTag const GSAxisTagUdln GSDeprecated("OTAxisTagUdln");
extern GSAxisTag const GSAxisTagVotf GSDeprecated("OTAxisTagVotf");
extern GSAxisTag const GSAxisTagVrot GSDeprecated("OTAxisTagVrot");
extern GSAxisTag const GSAxisTagWdth GSDeprecated("OTAxisTagWdth");
extern GSAxisTag const GSAxisTagWght GSDeprecated("OTAxisTagWght");
extern GSAxisTag const GSAxisTagXOPQ GSDeprecated("OTAxisTagXOPQ");
extern GSAxisTag const GSAxisTagXTAB GSDeprecated("OTAxisTagXTAB");
extern GSAxisTag const GSAxisTagXTCH GSDeprecated("OTAxisTagXTCH");
extern GSAxisTag const GSAxisTagXTRA GSDeprecated("OTAxisTagXTRA");
extern GSAxisTag const GSAxisTagYOPQ GSDeprecated("OTAxisTagYOPQ");
extern GSAxisTag const GSAxisTagYTAS GSDeprecated("OTAxisTagYTAS");
extern GSAxisTag const GSAxisTagYTCH GSDeprecated("OTAxisTagYTCH");
extern GSAxisTag const GSAxisTagYTDE GSDeprecated("OTAxisTagYTDE");
extern GSAxisTag const GSAxisTagYTFG GSDeprecated("OTAxisTagYTFG");
extern GSAxisTag const GSAxisTagYTLC GSDeprecated("OTAxisTagYTLC");
extern GSAxisTag const GSAxisTagYTRA GSDeprecated("OTAxisTagYTRA");
extern GSAxisTag const GSAxisTagYTSE GSDeprecated("OTAxisTagYTSE");
extern GSAxisTag const GSAxisTagYTUC GSDeprecated("OTAxisTagYTUC");
extern GSAxisTag const GSAxisTagYTFI GSDeprecated("OTAxisTagYTFI");
extern GSAxisTag const GSAxisTagROND GSDeprecated("OTAxisTagROND");
extern GSAxisTag const GSAxisTagGRAD GSDeprecated("OTAxisTagGRAD");

// roboto flex.
extern GSAxisTag const GSAxisTagXTSP GSDeprecated("OTAxisTagXTSP");
extern GSAxisTag const GSAxisTagXOUC GSDeprecated("OTAxisTagXOUC");
extern GSAxisTag const GSAxisTagXOLC GSDeprecated("OTAxisTagXOLC");
extern GSAxisTag const GSAxisTagXOFI GSDeprecated("OTAxisTagXOFI");
extern GSAxisTag const GSAxisTagXTUC GSDeprecated("OTAxisTagXTUC");
extern GSAxisTag const GSAxisTagXTLC GSDeprecated("OTAxisTagXTLC");
extern GSAxisTag const GSAxisTagXTFI GSDeprecated("OTAxisTagXTFI");
extern GSAxisTag const GSAxisTagXUCS GSDeprecated("OTAxisTagXUCS");
extern GSAxisTag const GSAxisTagXUCR GSDeprecated("OTAxisTagXUCR");
extern GSAxisTag const GSAxisTagXLCS GSDeprecated("OTAxisTagXLCS");
extern GSAxisTag const GSAxisTagXLCR GSDeprecated("OTAxisTagXLCR");
extern GSAxisTag const GSAxisTagXFIR GSDeprecated("OTAxisTagXFIR");
extern GSAxisTag const GSAxisTagYOUC GSDeprecated("OTAxisTagYOUC");
extern GSAxisTag const GSAxisTagYOLC GSDeprecated("OTAxisTagYOLC");
extern GSAxisTag const GSAxisTagYOFI GSDeprecated("OTAxisTagYOFI");
extern GSAxisTag const GSAxisTagYTOS GSDeprecated("OTAxisTagYTOS");
extern GSAxisTag const GSAxisTagYTUO GSDeprecated("OTAxisTagYTUO");
extern GSAxisTag const GSAxisTagYTLO GSDeprecated("OTAxisTagYTLO");
extern GSAxisTag const GSAxisTagYTFO GSDeprecated("OTAxisTagYTFO");
extern GSAxisTag const GSAxisTagYTDO GSDeprecated("OTAxisTagYTDO");
extern GSAxisTag const GSAxisTagYTAO GSDeprecated("OTAxisTagYTAO");
extern GSAxisTag const GSAxisTagXOAC GSDeprecated("OTAxisTagXOAC");
extern GSAxisTag const GSAxisTagYOAC GSDeprecated("OTAxisTagYOAC");
extern GSAxisTag const GSAxisTagYTUA GSDeprecated("OTAxisTagYTUA");
extern GSAxisTag const GSAxisTagYTLA GSDeprecated("OTAxisTagYTLA");
extern GSAxisTag const GSAxisTagYUAT GSDeprecated("OTAxisTagYUAT");
extern GSAxisTag const GSAxisTagYLAT GSDeprecated("OTAxisTagYLAT");
extern GSAxisTag const GSAxisTagUDLN GSDeprecated("OTAxisTagUDLN");
extern GSAxisTag const GSAxisTagYTTL GSDeprecated("OTAxisTagYTTL");
extern GSAxisTag const GSAxisTagXTTW GSDeprecated("OTAxisTagXTTW");

#pragma clang diagnostic pop

#if !defined(GLYPHS_LITE) && !defined(LIBCORE)
/**
 `GSAxis` represents an axis in a `GSFont` object.

 @attention This class is not to be confused with the `GXAxis` class!
 */
@interface GSAxis : NSObject <GSIdentifiableParameter, GSContainerProtocol>
#else
@interface GSAxis : NSObject <GSContainerProtocol>
#endif
{
#pragma GSAutoCodeStart ivars

#pragma GSAutoCodeEnd ivars
}

/**
 Convenience initializer (the properties are mutable after initialization).
 */
- (instancetype)initWithName:(NSString *)name tag:(nullable OTAxisTag)tag;

- (instancetype)initWithDict:(NSDictionary *)axisDict format:(GSFormatVersion)formatVersion;

#ifndef GLYPHS_VIEWER
- (nullable NSDictionary *)propertyListValueFormat:(GSFormatVersion)format error:(out NSError **)error;
#endif

/// `GSFont` for font-wide axes, `GSGlyph` for glyph-local axes.
@property (nonatomic, weak, nullable) id parent;

/**
 The human-readable axis name.
 */
@property (nonatomic, copy) NSString *name;

/**
 A tag representing the axis type.

 Typically, this value is a four-letter string, but for HOI purposes it may also be longer to differentiate between different axis with the same tag.
 Use `canonicalTag` for the four-letter tag.

 @attention This property can not be called `tag` as this conflicts with multiple existing properties/methods of the same name but type `NSInteger`. This would lead to build errors when accessing this property on objects of type `id`.
 */
@property (nonatomic, strong, nullable) OTAxisTag axisTag;
/**
 The four-character tag representing the axis type.
 
 Returns `nil`, if the tag specified by `axisTag` is not OpenType compliant even when truncated or padded with spaces to a length of four characters.
 */
@property (readonly, nonatomic, nullable) OTAxisTag canonicalTag;
/**
 Whether the axis has a four letter axis tag. Axis with longer tags are used for HOI.
 
 Note that `canonicalTag` might still be `nil` even when this property returns `YES` since `canonicalTag` only returns non-`nil` values that that are four-characters long **and** fulfils the other OpenType variation axis tag requirements. See ``axisTagIsOpenTypeCompliant:``.
 */
@property (readonly, nonatomic) BOOL isCanonical;

/// <https://learn.microsoft.com/en-us/typography/opentype/spec/dvaraxisreg#syntactic-requirements-for-design-variation-axis-tags>
+ (BOOL)axisTagIsOpenTypeCompliant:(OTAxisTag)tag;

- (nonnull NSString *)axisTagDefault;

/// shorter axisTag for UI purposes
- (NSString *)shortAxisTag;

+ (NSString *)shortAxisTagForTag:(NSString *)longTag;

@property (nonatomic, strong) NSString *axisId;

/**
 For variation axes, this is the default value.

 @note Minimum and maximum values are not currently accessed, so there are no properties for them yet.

 @attention This property can not be called `default` as this conflicts with a reserved keyword.
 */
@property (nonatomic) CGFloat defaultValue;

+ (NSArray *)defaultAxisTags;

+ (NSDictionary *)axisTagToNameMapping;

+ (NSDictionary *)axisNameToTagMapping;
/**
 TODO: Add documentation

 @attention This property can not be called `default` as this conflicts with a reserved keyword.
 */
@property (nonatomic) BOOL isDefault;

#ifndef GLYPHS_VIEWER
- (BOOL)saveToFile:(FILE *)file format:(GSFormatVersion)formatVersion context:(GSSaveContext *)context error:(NSError **)error;
#endif

+ (NSArray<GSAxis *> *)cornerAxes;

+ (nullable NSDictionary *)axisIdToTags:(NSDictionary *)region axes:(NSArray *)axes;

+ (nullable NSDictionary *)filterEmptyRegions:(NSDictionary *)region;

#pragma GSAutoCodeStart methods

#pragma mark Names

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

/** The number of names */
- (NSUInteger)countOfNames;

/** Returns object at idx in names

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

/** Returns the index of name in names

 @param name the object
 @return the index of the object
 */
- (NSUInteger)indexOfObjectInNames:(nonnull GSInfoValue *)name;

/** Adds the name

 @param name the object to be added
 */
- (void)addName:(nonnull GSInfoValue *)name;

/** Inserts the name at index into names

 @param name The object to insert
 @param idx The index
 */
- (void)insertObject:(nonnull GSInfoValue *)name inNamesAtIndex:(NSUInteger)idx;

/** Removes the name

 @param name the object to be removed
 */
- (void)removeObjectFromNames:(nonnull GSInfoValue *)name;

/** Removes the name at idx

 @param idx The index
 */
- (void)removeObjectFromNamesAtIndex:(NSUInteger)idx;
/// The hidden.
@property (nonatomic) BOOL hidden;

#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;

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

 @param key the key
 @return the data stored with key
 */
- (nullable NSObject *)userDataForKey:(nonnull 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:(nonnull NSString *)key;

/** Removed the object with this key.

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

#pragma mark TempData
/**
 a  dictionary that stores data. It will not be written to disk.
 */
@property (nonatomic, strong, null_resettable) 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 GSAutoCodeEnd methods
@end

NS_ASSUME_NONNULL_END
