#import "RecurrenceRule.h"
#import "DateRange.h"

@interface DateRecurrenceEnumerator : NSEnumerator
{
  icalrecur_iterator *_iterator;
}
- (id)initWithRule:(RecurrenceRule *)rule start:(Date *)start;
@end
@implementation DateRecurrenceEnumerator
- (id)initWithRule:(RecurrenceRule *)rule start:(Date *)start;
{
  if ((self = [super init]))
    _iterator = icalrecur_iterator_new([rule iCalRRule],  [start localICalTime]);
  return self;
}
- (id)nextObject
{
  struct icaltimetype next;
  
  next = icalrecur_iterator_next(_iterator);
  if (icaltime_is_null_time(next))
    return nil;
  return AUTORELEASE([[Date alloc] initWithICalTime:next]);
}
- (void)dealloc
{
  icalrecur_iterator_free(_iterator);
  [super dealloc];
}
@end

@interface DateRangeRecurrenceEnumerator : NSEnumerator
{
  icalrecur_iterator *_iterator;
  NSTimeInterval _length;
}
- (id)initWithRule:(RecurrenceRule *)rule start:(Date *)start length:(NSTimeInterval)length;
@end
@implementation DateRangeRecurrenceEnumerator
- (id)initWithRule:(RecurrenceRule *)rule start:(Date *)start length:(NSTimeInterval)length;
{
  if ((self = [super init])) {
    _iterator = icalrecur_iterator_new([rule iCalRRule],  [start localICalTime]);
    _length = length;
  }
  return self;
}
- (id)nextObject
{
  Date *date;
  struct icaltimetype next;
  
  next = icalrecur_iterator_next(_iterator);
  if (icaltime_is_null_time(next))
    return nil;
  date = AUTORELEASE([[Date alloc] initWithICalTime:next]);
  return AUTORELEASE([[DateRange alloc] initWithStart:date duration:_length]);
}
- (void)dealloc
{
  icalrecur_iterator_free(_iterator);
  [super dealloc];
}
@end

@implementation RecurrenceRule
- (id)init
{
  self = [super init];
  if (self)
    icalrecurrencetype_clear(&recur);
  return self;
}
- (id)initWithFrequency:(recurrenceFrequency)frequency
{
  NSAssert(frequency < recurrenceFrequenceOther, @"Wrong frequency");
  if ((self = [self init]))
    recur.freq = frequency;
  return self;
}
- (id)initWithFrequency:(recurrenceFrequency)frequency until:(Date *)endDate
{
  NSAssert(frequency < recurrenceFrequenceOther, @"Wrong frequency");
  NSAssert([endDate isDate], @"Works on dates");
  if ((self = [self init])) {
    /*
     * It's OK to use Date iCaltime: here as timezone modifications
     * only affect datetimes, not dates
     */
    recur.until = [endDate UTCICalTime];
    recur.freq = frequency;
  }
  return self;
}
- (id)initWithFrequency:(recurrenceFrequency)frequency count:(int)count
{
  NSAssert(frequency < recurrenceFrequenceOther, @"Wrong frequency");
  if ((self = [self init])) {
    recur.count = count;
    recur.freq = frequency;
  }
  return self;
}
/* FIXME : this one expect a day */
- (NSEnumerator *)enumeratorFromDate:(Date *)start
{
  NSAssert([start isDate], @"Works on dates");
  return AUTORELEASE([[DateRecurrenceEnumerator alloc] initWithRule:self start:start]);
}
/*
 * FIXME : and this one a datetime
 * We have to now if using datetimes will generate timezone problems
 * See DateRecurrenceEnumerator
 */
- (NSEnumerator *)enumeratorFromDate:(Date *)start length:(NSTimeInterval)length
{
  return AUTORELEASE([[DateRangeRecurrenceEnumerator alloc] initWithRule:self start:start length:length]);
}
- (recurrenceFrequency)frequency
{
  return recur.freq;
}
- (Date *)until
{
  if (icaltime_is_null_time(recur.until))
    return nil;
  return AUTORELEASE([[Date alloc] initWithICalTime:recur.until]);
}
- (int)count
{
  return recur.count;
}
@end

@implementation RecurrenceRule(iCalendar)
- (id)initWithICalRRule:(struct icalrecurrencetype)rrule
{
  if ((self = [super init]))
    recur = rrule;
  return self;
}
- (id)initWithICalString:(NSString *)rrule
{
  if ((self = [super init]))
    recur = icalrecurrencetype_from_string([rrule cString]);
  return self;
}
- (struct icalrecurrencetype)iCalRRule
{
  return recur;
}
@end

@implementation RecurrenceRule(NSCoding)
- (void)encodeWithCoder:(NSCoder *)coder
{
  [coder encodeObject:[NSString stringWithCString:icalrecurrencetype_as_string(&recur)] forKey:@"rrule"];
}
- (id)initWithCoder:(NSCoder *)coder
{
  recur = icalrecurrencetype_from_string([[coder decodeObjectForKey:@"rrule"] cString]);
  return self;
}
@end
