//
//  CyTVClient.m
//  CyTV Client
//
//  Created by Andreas Junghans on Sat Oct 25 2003.
//  Copyright (c) 2003-2004 Andreas Junghans. All rights reserved.
//

#import "StreamsDataSource.h"

static NSString* idKey = @"id";
static NSString* byteCountKey = @"byteCount";
static NSString* recordingFlagKey = @"recordingFlag";
static NSString* durationKey = @"duration";
static NSString* qualityKey = @"quality";
static NSString* titleKey = @"title";
static NSString* episodeKey = @"episode";
static NSString* channelKey = @"channel";
static NSString* dateKey = @"date";

static int sortedDescendingFactor = 1;

int compareById(NSDictionary* first, NSDictionary* second, void* context) {
    return [(NSString*)[first objectForKey:idKey] compare:
            (NSString*)[second objectForKey:idKey]]*sortedDescendingFactor;
}

int compareByRecordingFlag(NSDictionary* first, NSDictionary* second, void* context) {
    int temp = [(NSString*)[first objectForKey:recordingFlagKey] compare:
                (NSString*)[second objectForKey:recordingFlagKey]]*sortedDescendingFactor;
    if (temp == NSOrderedSame) {
        return compareById(first, second, context);
    }
    return temp;
}

int compareByDuration(NSDictionary* first, NSDictionary* second, void* context) {
    int temp = [(NSString*)[first objectForKey:durationKey] compare:
                (NSString*)[second objectForKey:durationKey]]*sortedDescendingFactor;
    if (temp == NSOrderedSame) {
        return compareById(first, second, context);
    }
    return temp;
}

int compareByQuality(NSDictionary* first, NSDictionary* second, void* context) {
    int temp = [(NSString*)[first objectForKey:qualityKey] compare:
                (NSString*)[second objectForKey:qualityKey]]*sortedDescendingFactor;
    if (temp == NSOrderedSame) {
        return compareById(first, second, context);
    }
    return temp;
}

int compareByTitle(NSDictionary* first, NSDictionary* second, void* context) {
    NSString* firstTitle = [first objectForKey:titleKey];
    NSString* secondTitle = [second objectForKey:titleKey];
    int temp = [firstTitle caseInsensitiveCompare:
                secondTitle]*sortedDescendingFactor;
    if (temp == NSOrderedSame) {
        return compareById(first, second, context);
    }
    if (sortedDescendingFactor == 1) {
        if ([firstTitle isEqualToString:@""] || [secondTitle isEqualToString:@""]) {
            return -temp;
        }
    }
    return temp;
}

int compareByEpisode(NSDictionary* first, NSDictionary* second, void* context) {
    NSString* firstEpisode = [first objectForKey:episodeKey];
    NSString* secondEpisode = [second objectForKey:episodeKey];
    int temp = [firstEpisode caseInsensitiveCompare:
                secondEpisode]*sortedDescendingFactor;
    if (temp == NSOrderedSame) {
        return compareByTitle(first, second, context);
    }
    if (sortedDescendingFactor == 1) {
        if ([firstEpisode isEqualToString:@""] || [secondEpisode isEqualToString:@""]) {
            return -temp;
        }
    }
    return temp;
}

int compareByChannel(NSDictionary* first, NSDictionary* second, void* context) {
    NSString* firstChannel = [first objectForKey:channelKey];
    NSString* secondChannel = [second objectForKey:channelKey];
    int temp = [firstChannel caseInsensitiveCompare:
                secondChannel]*sortedDescendingFactor;
    if (temp == NSOrderedSame) {
        return compareById(first, second, context);
    }
    if (sortedDescendingFactor == 1) {
        if ([firstChannel isEqualToString:@""] || [secondChannel isEqualToString:@""]) {
            return -temp;
        }
    }
    return temp;
}

int compareByDate(NSDictionary* first, NSDictionary* second, void* context) {
    return compareById(first, second, context);
}

static int (*sortFunction)(id, id, void *) = compareByDate;
static NSDictionary* sortFunctionDict;

@implementation StreamsDataSource

- (id)init {
    if (self = [super init]) {
        _streams = [[NSArray array] retain];
        _streamsDict = [[NSMutableDictionary alloc] init];
        _currentChannel = -1;
        sortFunctionDict = [[NSDictionary dictionaryWithObjectsAndKeys:
                             [NSValue valueWithPointer:compareByDate], dateKey,
                             [NSValue valueWithPointer:compareByRecordingFlag], recordingFlagKey,
                             [NSValue valueWithPointer:compareByDuration], durationKey,
                             [NSValue valueWithPointer:compareByQuality], qualityKey,
                             [NSValue valueWithPointer:compareByChannel], channelKey,
                             [NSValue valueWithPointer:compareByTitle], titleKey,
                             [NSValue valueWithPointer:compareByEpisode], episodeKey,
                             nil]
                            retain];
        return self;
    }
    return nil;
}

- (int)numberOfRowsInTableView:(NSTableView *)aTableView {
    return [_streams count];
}

- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex {
    return [[_streams objectAtIndex: rowIndex] objectForKey: [aTableColumn identifier]];
}

- (NSString*)streamIdForRow:(int)row {
    return [[_streams objectAtIndex: row] objectForKey:idKey];
}

- (int)rowForStreamId:(NSString*)streamId {
    int i;
    int count = [_streams count];
    for (i = 0; i < count; ++i) {
        if ([[[_streams objectAtIndex:i] objectForKey:idKey]
             isEqualToString:streamId]) {
            return i;
        }
    }
    return -1;
}

- (double)streamSizeForRow:(int)row {
    return [[[_streams objectAtIndex: row] objectForKey:byteCountKey] doubleValue];
}

- (NSString*)streamQualityForRow:(int)row {
    return [[_streams objectAtIndex: row] objectForKey:qualityKey];
}

- (NSString*)titleForRow:(int)row {
    NSString* title = [[_streams objectAtIndex:row] objectForKey:titleKey];
    NSString* episode = [[_streams objectAtIndex:row] objectForKey:episodeKey];
    if (episode != nil) {
        if ([episode length] > 0) {
            title = [NSString stringWithFormat:@"%@ - %@", (title == nil ? @"" : title), episode];
        }
    }
    return title;
}

- (NSString*)recordingFlagForRow:(int)row {
    return [[_streams objectAtIndex:row] objectForKey:recordingFlagKey];
}

- (void)updateWithInfo:(NSString*)info channels:(NSPopUpButton*)channels {
    NSArray* streams = [info componentsSeparatedByString: @"\n"];
    //[_streams release];
    //_streams = [[NSMutableArray alloc] init];
    NSString *stream, *streamId, *seconds, *recorded, *quality;
    NSString *title, *episode, *channel, *temp, *currentStreamId = nil;
	NSDate* date;
    NSArray* streamInfo;
    NSDictionary *streamEntry;
    int channelId, index;
    int hoursInt, minutesInt, secondsInt;
    double byteCount, currentBytePos = 0;
    int i;
    for (i = 0; i < [streams count]; ++i) {
        stream = [streams objectAtIndex: i];
        if ([stream length] > 0 && ![stream hasPrefix:@"@"]) {
            streamInfo = [stream componentsSeparatedByString: @":"];
            if ([streamInfo count] == 1) {
                // no colon means we have information about a stream
                temp = [streamInfo objectAtIndex:0];
                if ([temp hasPrefix:@"-"]) {
                    if ([temp length] > 1) {
                        // remove a stream from the dictionary
                        [_streamsDict removeObjectForKey:
                         [temp substringFromIndex:1]];
                    }
                } else {
                    streamInfo = [[streamInfo objectAtIndex: 0] componentsSeparatedByString: @","];
                    if ([streamInfo count] == 1) {
                        // no comma: this is additional information
                        _currentChannel = [[streamInfo objectAtIndex: 0] intValue];
                    } else if ([streamInfo count] >= 5) {
                        streamId = [streamInfo objectAtIndex: 0];
                        //firstVariableStreamId = streamId;
                        byteCount = [[streamInfo objectAtIndex: 1] doubleValue];
                        seconds = [streamInfo objectAtIndex: 2];
                        recorded = [streamInfo objectAtIndex: 3];
                        title = @"";
                        episode = @"";
                        channel = @"";
                        date = nil;
                        if ([recorded isEqualToString: @"0"]) {
                            recorded = @"L";
                        } else {
                            recorded = @"R";
                            if (i < [streams count]-1) {
                                temp = [streams objectAtIndex: i+1];
                                if ([temp rangeOfString: @"@"].location == 0) {
                                    ++i;
                                    title = [temp substringFromIndex: 1];
                                }
                            }
                            if (i < [streams count]-1) {
                                temp = [streams objectAtIndex: i+1];
                                if ([temp rangeOfString: @"@"].location == 0) {
                                    ++i;
                                    channel = [temp substringFromIndex: 1];
                                }
                            }
                            if (i < [streams count]-1) {
                                temp = [streams objectAtIndex: i+1];
                                if ([temp rangeOfString: @"@"].location == 0) {
                                    ++i;
                                    if ([temp length] > 1) {
                                        date = [NSDate dateWithString:[temp substringFromIndex: 1]];
                                    }
                                }
                            }
                            if (i < [streams count]-1) {
                                //printf("Extended recording info found\n");
                                temp = [streams objectAtIndex: i+1];
                                if ([temp rangeOfString: @"@"].location == 0) {
                                    ++i;
                                    if ([temp length] > 1) {
                                        channelId = [[temp substringFromIndex: 1] intValue];
                                        // hack: override the channel with the manually
                                        // determined channel name (workaround for newer
                                        // EyeTV versions not returning channel names
                                        // anymore)
                                        index = [channels indexOfItemWithTag:channelId];
                                        if (index != -1) {
                                            channel = [[channels itemAtIndex:index] title];
                                            channel = [[[channel componentsSeparatedByString:@":"] objectAtIndex:1]
                                                     stringByTrimmingCharactersInSet:
                                                     [NSCharacterSet whitespaceCharacterSet]];
                                        }
                                    }
                                }
                            }
                            if (i < [streams count]-1) {
                                temp = [streams objectAtIndex:i+1];
                                if ([temp rangeOfString:@"@"].location == 0) {
                                    ++i;
                                    episode = [temp substringFromIndex:1];
                                }
                            }
                        }
                        quality = [streamInfo objectAtIndex: 4];
                        secondsInt = [seconds intValue];
                        hoursInt = secondsInt / 3600;
                        secondsInt %= 3600;
                        minutesInt = secondsInt / 60;
                        secondsInt %= 60;
                        seconds = [NSString stringWithFormat: @"%d:%0.2d:%0.2d",
                                                              hoursInt,
                                                              minutesInt,
                                                              secondsInt];
                        streamEntry = [NSDictionary dictionaryWithObjectsAndKeys:
                                                    streamId, idKey,
                                                    [NSNumber numberWithDouble: byteCount], byteCountKey,
                                                    recorded, recordingFlagKey,
                                                    seconds, durationKey,
                                                    quality, qualityKey,
                                                    title, titleKey,
                                                    episode, episodeKey,
                                                    channel, channelKey,
                                                    date, dateKey,
                                                    nil];
                        [_streamsDict setObject:streamEntry forKey:streamId];
                    }   // if the sanity check for stream information worked
                }   // if we don't have an "@" prefix
            } else {
                // we have additional information (beyond the list of
                // available streams)
                streamInfo = [[streamInfo objectAtIndex: 1] componentsSeparatedByString: @","];
                if ([streamInfo count] >= 2) {
                    currentStreamId = [streamInfo objectAtIndex: 0];
                    currentBytePos = [[streamInfo objectAtIndex: 1] doubleValue];
                }   // if the sanity check for the current position worked
            }   // if we have information about the current position in the stream
        }   // if we have any information at all (and not an empty line)
    }   // iterate over the lines of the stream information
    [_streams release];
    _streams = [_streamsDict allValues];
    _streams = [[_streams sortedArrayUsingFunction:sortFunction context:nil] retain];
    _currentStreamIndex = -1;
    if (currentStreamId != nil) {
        for (i = 0; i < [_streams count]; ++i) {
            streamEntry = [_streams objectAtIndex:i];
            if ([currentStreamId isEqualToString:[streamEntry objectForKey:idKey]]) {
                _currentStreamIndex = i;
                byteCount = [[streamEntry objectForKey:byteCountKey] doubleValue];
                if (currentBytePos <= byteCount) {     // sanity check
                    _currentStreamPosition = currentBytePos / byteCount * 100.0;
                } else {
                    _currentStreamPosition = 100.0;
                    // this can happen since file sizes are only
                    // evaluated once every second
                }
                break;
            }
        }
    }
}

- (int)currentStreamIndex {
    return _currentStreamIndex;
}

- (double)currentStreamPosition {
    return _currentStreamPosition;
}

- (BOOL)isRecording {
    if ([_streams count] == 0) {
        return FALSE;
    }
    return [[[_streams lastObject] objectForKey:recordingFlagKey] isEqualToString:@"R"];
}


/**
 * Change the sort order.
 */

- (void)sortStreamsByColumn:(NSTableColumn*)column descending:(BOOL)descending {
    NSValue* sortFunctionValue = [sortFunctionDict objectForKey:[column identifier]];
    if (sortFunctionValue == nil) {
        return;
    }
    sortFunction = [sortFunctionValue pointerValue];
    sortedDescendingFactor = (descending ? -1 : 1);
    NSString* currentStreamId = nil;
    if (_currentStreamIndex != -1) {
        currentStreamId = [self streamIdForRow:_currentStreamIndex];
    }
    [_streams release];
    _streams = [_streamsDict allValues];
    _streams = [[_streams sortedArrayUsingFunction:sortFunction context:nil] retain];
    _currentStreamIndex = -1;
    if (currentStreamId != nil) {
        NSDictionary* streamEntry;
        int i;
        for (i = 0; i < [_streams count]; ++i) {
            streamEntry = [_streams objectAtIndex:i];
            if ([currentStreamId isEqualToString:[streamEntry objectForKey:idKey]]) {
                _currentStreamIndex = i;
                break;
            }
        }
    }
}


/**
 * Returns the current channel. The return value is only sensible
 * after updateWithInfo: has been called at least once.
 */

- (int)currentChannel {
    return _currentChannel;
}
    
@end
