/*
* This file is part of the YSSDWebImage package.
* (c) Olivier Poitrey <rs@dailymotion.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

#import "YSSDImageHEICCoder.h"
#import "YSSDImageHEICCoderInternal.h"

// These constants are available from iOS 13+ and Xcode 11. This raw value is used for toolchain and firmware compatibility
static NSString * kYSSDCGImagePropertyHEICYSSDictionary = @"{HEICS}";
static NSString * kYSSDCGImagePropertyHEICSLoopCount = @"LoopCount";
static NSString * kYSSDCGImagePropertyHEICYSSDelayTime = @"DelayTime";
static NSString * kYSSDCGImagePropertyHEICSUnclampedDelayTime = @"UnclampedDelayTime";

@implementation YSSDImageHEICCoder

+ (void)initialize {
#if __IPHONE_13_0 || __TVOS_13_0 || __MAC_10_15 || __WATCHOS_6_0
    // Xcode 11
    if (@available(iOS 13, tvOS 13, macOS 10.15, watchOS 6, *)) {
        // Use YSSDK instead of raw value
        kYSSDCGImagePropertyHEICYSSDictionary = (__bridge NSString *)kCGImagePropertyHEICSDictionary;
        kYSSDCGImagePropertyHEICSLoopCount = (__bridge NSString *)kCGImagePropertyHEICSLoopCount;
        kYSSDCGImagePropertyHEICYSSDelayTime = (__bridge NSString *)kCGImagePropertyHEICSDelayTime;
        kYSSDCGImagePropertyHEICSUnclampedDelayTime = (__bridge NSString *)kCGImagePropertyHEICSUnclampedDelayTime;
    }
#endif
}

+ (instancetype)sharedCoder {
    static YSSDImageHEICCoder *coder;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        coder = [[YSSDImageHEICCoder alloc] init];
    });
    return coder;
}

#pragma mark - YSSDImageCoder

- (BOOL)canDecodeFromData:(nullable NSData *)data {
    switch ([NSData yssd_imageFormatForImageData:data]) {
        case YSSDImageFormatHEIC:
            // Check HEIC decoding compatibility
            return [self.class canDecodeFromHEICFormat];
        case YSSDImageFormatHEIF:
            // Check HEIF decoding compatibility
            return [self.class canDecodeFromHEIFFormat];
        default:
            return NO;
    }
}

- (BOOL)canIncrementalDecodeFromData:(NSData *)data {
    return [self canDecodeFromData:data];
}

- (BOOL)canEncodeToFormat:(YSSDImageFormat)format {
    switch (format) {
        case YSSDImageFormatHEIC:
            // Check HEIC encoding compatibility
            return [self.class canEncodeToHEICFormat];
        case YSSDImageFormatHEIF:
            // Check HEIF encoding compatibility
            return [self.class canEncodeToHEIFFormat];
        default:
            return NO;
    }
}

#pragma mark - HEIF Format

+ (BOOL)canDecodeFromFormat:(YSSDImageFormat)format {
    CFStringRef imageUTType = [NSData yssd_UTTypeFromImageFormat:format];
    NSArray *imageUTTypes = (__bridge_transfer NSArray *)CGImageSourceCopyTypeIdentifiers();
    if ([imageUTTypes containsObject:(__bridge NSString *)(imageUTType)]) {
        return YES;
    }
    return NO;
}

+ (BOOL)canDecodeFromHEICFormat {
    static BOOL canDecode = NO;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        canDecode = [self canDecodeFromFormat:YSSDImageFormatHEIC];
    });
    return canDecode;
}

+ (BOOL)canDecodeFromHEIFFormat {
    static BOOL canDecode = NO;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        canDecode = [self canDecodeFromFormat:YSSDImageFormatHEIF];
    });
    return canDecode;
}

+ (BOOL)canEncodeToFormat:(YSSDImageFormat)format {
    NSMutableData *imageData = [NSMutableData data];
    CFStringRef imageUTType = [NSData yssd_UTTypeFromImageFormat:format];
    
    // Create an image destination.
    CGImageDestinationRef imageDestination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)imageData, imageUTType, 1, NULL);
    if (!imageDestination) {
        // Can't encode to HEIC
        return NO;
    } else {
        // Can encode to HEIC
        CFRelease(imageDestination);
        return YES;
    }
}

+ (BOOL)canEncodeToHEICFormat {
    static BOOL canEncode = NO;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        canEncode = [self canEncodeToFormat:YSSDImageFormatHEIC];
    });
    return canEncode;
}

+ (BOOL)canEncodeToHEIFFormat {
    static BOOL canEncode = NO;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        canEncode = [self canEncodeToFormat:YSSDImageFormatHEIF];
    });
    return canEncode;
}

#pragma mark - Subclass Override

+ (YSSDImageFormat)imageFormat {
    return YSSDImageFormatHEIC;
}

+ (NSString *)imageUTType {
    return (__bridge NSString *)kYSSDUTTypeHEIC;
}

+ (NSString *)dictionaryProperty {
    return kYSSDCGImagePropertyHEICYSSDictionary;
}

+ (NSString *)unclampedDelayTimeProperty {
    return kYSSDCGImagePropertyHEICSUnclampedDelayTime;
}

+ (NSString *)delayTimeProperty {
    return kYSSDCGImagePropertyHEICYSSDelayTime;
}

+ (NSString *)loopCountProperty {
    return kYSSDCGImagePropertyHEICSLoopCount;
}

+ (NSUInteger)defaultLoopCount {
    return 0;
}

@end
