//
//  RecordingInfo.m
//  CyTV
//
//  Created by Andreas Junghans on 17.12.07.
//  Copyright 2007 __MyCompanyName__. All rights reserved.
//

#import "RecordingInfo.h"
#import "AppleScript.h"
#import "JSON.h"


static NSMutableArray *_recordingInfosSortedById = nil;
static NSMutableArray *_recordingInfosSortedByTitle = nil;
static NSMutableDictionary *_imageCache = nil;

@implementation RecordingInfo

+ (void)initialize {
    if (self == [RecordingInfo class]) {
        _recordingInfosSortedById = [[NSMutableArray alloc] init];
        _recordingInfosSortedByTitle = [[NSMutableArray alloc] init];
        _imageCache = [[NSMutableDictionary alloc] init];
    }
}

- (void)dealloc {
    [_title release];
    [_episode release];
    [_location release];
    [_previewPicture release];
    [_imageCacheKey release];
    [super dealloc];
}

- (void)setId:(UInt64)id {
    _id = id;
}
- (UInt64)getId {
    return _id;
}

- (void)setTitle:(NSString *)title {
    if (title == nil) {
        title = @"";
    }
    [_title autorelease];
    _title = [title retain];
}
- (NSString *)getTitle {
    return _title;
}

- (void)setEpisode:(NSString *)episode {
    if (episode == nil) {
        episode = @"";
    }
    [_episode autorelease];
    _episode = [episode retain];
}
- (NSString *)getEpisode {
    return _episode;
}

- (void)setLocation:(NSURL *)location {
    [_location autorelease];
    _location = [location retain];
}
- (NSURL *)getLocation {
    return _location;
}

- (NSString *) getVideoFilePath {
    return [[[_location path] stringByDeletingPathExtension] stringByAppendingPathExtension:@"mpg"];
}

- (void)setPreviewPicture:(NSImage *)previewPicture {
    [_previewPicture autorelease];
    _previewPicture = [previewPicture retain];
}
- (NSImage *)getPreviewPicture {
    return _previewPicture;
}

- (void)setImageCacheKey:(NSString *)imageCacheKey {
    [_imageCacheKey autorelease];
    _imageCacheKey = [imageCacheKey retain];
}
- (NSString *)getImageCacheKey {
    return _imageCacheKey;
}

- (NSComparisonResult)compareToById:(RecordingInfo *)anotherObject {
    UInt64 otherId = [anotherObject getId];
    if (_id < otherId) {
        return NSOrderedAscending;
    }
    if (_id > otherId) {
        return NSOrderedDescending;
    }
    return NSOrderedSame;
}

- (NSComparisonResult)compareToByTitle:(RecordingInfo *)anotherObject {
    NSComparisonResult result = [_title localizedCompare:[anotherObject getTitle]];
    if (result == NSOrderedSame) {
        result = [_episode localizedCompare:[anotherObject getEpisode]];
    }
    return result;
}

- (NSDictionary *)toDictionary {
    return [NSDictionary dictionaryWithObjectsAndKeys:
        [NSNumber numberWithUnsignedLongLong:_id], @"id", _title, @"title", _episode, @"episode",
        nil];
}

+ (void)collectRecordingInfo {
    NSAppleEventDescriptor *desc;

    // reset the array
    NSMutableSet *imageCacheKeysToRemove = [[NSMutableSet alloc] init];
    unsigned int recordingCount = [_recordingInfosSortedById count];
    unsigned int i;
    NSString *imageCacheKey;
    for (i = 0; i < recordingCount; ++i) {
        imageCacheKey = [[_recordingInfosSortedById objectAtIndex:i] getImageCacheKey];
        if (imageCacheKey != nil) {
            [imageCacheKeysToRemove addObject:imageCacheKey];
        }
    }
    [_recordingInfosSortedById removeAllObjects];
    [_recordingInfosSortedByTitle removeAllObjects];

    @try {
        // first get the number of recordings
        // (because if we access "every recording" later on, it will fail if there are no recordings)
        desc = [AppleScript executeScript:@"tell application \"EyeTV\" to return count of recordings"];
        SInt32 recordingCount = [desc int32Value];
        
        // now get the recording info we want
        // TODO: add error checking
        if (recordingCount > 0) {
            desc = [AppleScript executeScript:@"tell application \"EyeTV\" to return {unique id, title, episode, location, preview picture} of every recording"];
            NSAppleEventDescriptor* idList = [desc descriptorAtIndex:1];
            NSAppleEventDescriptor* titleList = [desc descriptorAtIndex:2];
            NSAppleEventDescriptor* episodeList = [desc descriptorAtIndex:3];
            NSAppleEventDescriptor* locationList = [desc descriptorAtIndex:4];
            NSAppleEventDescriptor* previewPictureList = [desc descriptorAtIndex:5];
            RecordingInfo *recordingInfo;
            int i;
            for (i = 1; i <= recordingCount; ++i) {
                recordingInfo = [[[RecordingInfo alloc] init] autorelease];
                double recordingIdDouble;   // = *(double *)[[[idList descriptorAtIndex:i] data] bytes];
                [[[[idList descriptorAtIndex:i] coerceToDescriptorType:typeIEEE64BitFloatingPoint] data] getBytes:&recordingIdDouble];
                UInt64 recordingId = (UInt64)recordingIdDouble;
                [recordingInfo setId:recordingId];
                [recordingInfo setTitle:[[titleList descriptorAtIndex:i] stringValue]];
                [recordingInfo setEpisode:[[episodeList descriptorAtIndex:i] stringValue]];
                NSData *locationData = [[locationList descriptorAtIndex:i] data];
                NSString *location = [[NSString alloc] initWithBytes:[locationData bytes] length:[locationData length] encoding:NSUTF8StringEncoding];
                [recordingInfo setLocation:[NSURL URLWithString:location]];
                [location release];
                NSData *imageData = [[previewPictureList descriptorAtIndex:i] data];
                NSImage *image = [[[NSImage alloc] initWithData:imageData] autorelease];
                if ([image isValid]) {
                    NSString *imageCacheKey = [NSString stringWithFormat:@"%llx_%llx", (unsigned long long)recordingId, (unsigned long long)[imageData length]];
                    [imageCacheKeysToRemove removeObject:imageCacheKey];
                    [recordingInfo setImageCacheKey:imageCacheKey];
                    [recordingInfo setPreviewPicture:image];
                    //printf("Image cache key: %s / isValid: %d\n", [imageCacheKey UTF8String], (int)[image isValid]);
                }
                // --> test
                //Class imageRepClass = [NSImageRep imageRepClassForData:[[previewPictureList descriptorAtIndex:i] data]];
                //NSImageRep *imageRep = [[NSBitmapImageRep alloc] initWithData:[image TIFFRepresentation]];
                //printf("Image width: %d\n", (int)[imageRep size].width);
                //[imageRep release];
                // <--
                [_recordingInfosSortedById addObject:recordingInfo];
                [_recordingInfosSortedByTitle addObject:recordingInfo];
            }
            [_recordingInfosSortedById sortUsingSelector:@selector(compareToById:)];
            [_recordingInfosSortedByTitle sortUsingSelector:@selector(compareToByTitle:)];
        }
    } @catch (NSException *e) {
        [_recordingInfosSortedById removeAllObjects];
        [_recordingInfosSortedByTitle removeAllObjects];
    }
    NSEnumerator *enumerator = [imageCacheKeysToRemove objectEnumerator];
    id value;
    while ((value = [enumerator nextObject])) {
        [_imageCache removeObjectForKey:value];
    }
    [imageCacheKeysToRemove release];
}

+ (RecordingInfo *)getRecordingInfoById:(UInt64)recordingId {
    unsigned long recordingCount = [_recordingInfosSortedById count];
    unsigned long i;
    RecordingInfo *recordingInfo;
    for (i = 0; i < recordingCount; ++i) {
        recordingInfo = [_recordingInfosSortedById objectAtIndex:i];
        if ([recordingInfo getId] == recordingId) {
            return recordingInfo;
        }
    }
    return nil;
}

+ (RecordingInfo *)getRecordingInfoBySearch:(NSString *)term {
    unsigned long recordingCount = [_recordingInfosSortedById count];
    unsigned long i;
    RecordingInfo *recordingInfo;
    for (i = 0; i < recordingCount; ++i) {
        recordingInfo = [_recordingInfosSortedById objectAtIndex:i];
        if ([[recordingInfo getTitle] rangeOfString:term options:NSCaseInsensitiveSearch].location != NSNotFound) {
            return recordingInfo;
        }
    }
    return nil;
}

+ (NSData *)getPreviewPNGForId:(UInt64)recordingId {
    unsigned long recordingCount = [_recordingInfosSortedById count];
    unsigned long i;
    RecordingInfo *recordingInfo;
    for (i = 0; i < recordingCount; ++i) {
        recordingInfo = [_recordingInfosSortedById objectAtIndex:i];
        if ([recordingInfo getId] == recordingId) {
            NSImage *previewImage = [recordingInfo getPreviewPicture];
            if (previewImage != nil) {
                NSString *imageCacheKey = [recordingInfo getImageCacheKey];
                NSData *png = [_imageCache objectForKey:imageCacheKey];
                if (png == nil) {
                    NSBitmapImageRep *converter = [[NSBitmapImageRep alloc] initWithData:[previewImage TIFFRepresentation]];
                    png = [converter representationUsingType:NSPNGFileType properties:nil];
                    [_imageCache setObject:png forKey:imageCacheKey];
                    //printf("PNG length: %d\n", (int)[png length]);
                }
                return png;
            }
            break;
        }
    }
    return nil;
}

+ (NSString *)getJSONForRecordingsFrom:(unsigned long)fromIndex to:(unsigned long)toIndex sortedBy:(SortRecordingsBy)sortedBy ascending:(BOOL)ascending {
    NSArray *source = _recordingInfosSortedById;
    switch (sortedBy) {
        case SortByTitle:
            source = _recordingInfosSortedByTitle;
            break;
    }
    unsigned long totalCount = [source count];
    NSMutableArray *results = [[NSMutableArray alloc] init];
    if (totalCount > 0) {
        if (fromIndex < 0) {
            fromIndex = 0;
        }
        if (toIndex >= totalCount) {
            toIndex = totalCount - 1;
        }
        if (fromIndex <= toIndex) {
            unsigned long i;
            if (ascending) {
                for (i = fromIndex; i <= toIndex; ++i) {
                    [results addObject:[[source objectAtIndex:i] toDictionary]];
                }
            } else {
                fromIndex = totalCount - fromIndex - 1;
                toIndex = totalCount - toIndex - 1;
                for (i = fromIndex; i >= toIndex; --i) {
                    [results addObject:[[source objectAtIndex:i] toDictionary]];
                }
            }
        }
    }
    NSDictionary *resultContainer = [NSDictionary dictionaryWithObjectsAndKeys:
        [NSNumber numberWithUnsignedLong:totalCount], @"totalCount",
        results, @"results",
        nil];
    return [resultContainer JSONRepresentation];
}

@end
