#import "CPTTimeFormatter.h"

/** @brief A number formatter that converts time intervals to dates.
 *  Useful for formatting labels on an axis. If you choose the numerical
 *  scale of the plot space to be in seconds, axis labels can be directly
 *  generated by setting a CPTTimeFormatter as the @link CPTAxis::labelFormatter labelFormatter @endlink
 *  and/or @link CPTAxis::minorTickLabelFormatter minorTickLabelFormatter @endlink.
 **/
@implementation CPTTimeFormatter

/** @property nullable NSDateFormatter *dateFormatter
 *  @brief The date formatter used to generate strings from time intervals.
 **/
@synthesize dateFormatter;

/** @property nullable NSDate *referenceDate
 *  @brief Date from which time intervals are taken.
 *  If @nil, the standard reference date (1 January 2001, GMT) is used.
 **/
@synthesize referenceDate;

#pragma mark -
#pragma mark Init/Dealloc

/// @name Initialization
/// @{

/** @brief Initializes new instance with a default date formatter.
 *  The default formatter uses @ref NSDateFormatterMediumStyle for dates and times.
 *  @return The new instance.
 **/
-(nonnull instancetype)init
{
    NSDateFormatter *newDateFormatter = [[NSDateFormatter alloc] init];

    newDateFormatter.dateStyle = NSDateFormatterMediumStyle;
    newDateFormatter.timeStyle = NSDateFormatterMediumStyle;

    self = [self initWithDateFormatter:newDateFormatter];

    return self;
}

/// @}

/** @brief Initializes new instance with the date formatter passed.
 *  @param aDateFormatter The date formatter.
 *  @return The new instance.
 **/
-(nonnull instancetype)initWithDateFormatter:(nullable NSDateFormatter *)aDateFormatter
{
    if ((self = [super init])) {
        dateFormatter = aDateFormatter;
        referenceDate = nil;
    }
    return self;
}

#pragma mark -
#pragma mark NSCoding Methods

/// @cond

-(void)encodeWithCoder:(nonnull NSCoder *)coder
{
    [super encodeWithCoder:coder];

    [coder encodeObject:self.dateFormatter forKey:@"CPTTimeFormatter.dateFormatter"];
    [coder encodeObject:self.referenceDate forKey:@"CPTTimeFormatter.referenceDate"];
}

/// @endcond

/** @brief Returns an object initialized from data in a given unarchiver.
 *  @param coder An unarchiver object.
 *  @return An object initialized from data in a given unarchiver.
 */
-(nullable instancetype)initWithCoder:(nonnull NSCoder *)coder
{
    if ((self = [super init])) {
        dateFormatter = [coder decodeObjectForKey:@"CPTTimeFormatter.dateFormatter"];
        referenceDate = [[coder decodeObjectForKey:@"CPTTimeFormatter.referenceDate"] copy];
    }
    return self;
}

#pragma mark -
#pragma mark NSCopying Methods

/// @cond

-(nonnull id)copyWithZone:(nullable NSZone *)zone
{
    CPTTimeFormatter *newFormatter = [[CPTTimeFormatter allocWithZone:zone] init];

    if ( newFormatter ) {
        newFormatter.dateFormatter = self.dateFormatter;
        newFormatter.referenceDate = self.referenceDate;
    }
    return newFormatter;
}

/// @endcond

#pragma mark -
#pragma mark Formatting

/// @name Formatting
/// @{

/**
 *  @brief Converts decimal number for the time into a date string.
 *  Uses the date formatter to do the conversion. Conversions are relative to the
 *  reference date, unless it is @nil, in which case the standard reference date
 *  of 1 January 2001, GMT is used.
 *  @param coordinateValue The time value.
 *  @return The date string.
 **/
-(nullable NSString *)stringForObjectValue:(nullable id)coordinateValue
{
    NSString *string = nil;

    if ( [coordinateValue respondsToSelector:@selector(doubleValue)] ) {
        NSDate *date;
        NSDate *refDate = self.referenceDate;

        if ( refDate ) {
            date = [[NSDate alloc] initWithTimeInterval:[coordinateValue doubleValue] sinceDate:refDate];
        }
        else {
            date = [[NSDate alloc] initWithTimeIntervalSinceReferenceDate:[coordinateValue doubleValue]];
        }
        string = [self.dateFormatter stringFromDate:date];
    }

    return string;
}

/// @}

@end
