text:
@implementation LBBridge

- (id)initWithUniqueID:(NSString *)uniqueID ipAddress:(NSString *)ipAddress
{
    self = [super init];

    if (self) {
        self.uniqueID = uniqueID;
        self.ipAddress = ipAddress;
        self.lastRefresh = [NSDate date];
        [self flush];
    }

    return self;
}

- (void)flush
{
    self.macAddress = nil;
    self.dhcp = nil;
    self.netmask = nil;
    self.gatewayAddress = nil;
    self.proxyAddress = nil;
    self.proxyPort = nil;
    self.softwareVersion = nil;
    self.updateNotify = nil;
    self.updateState = nil;
    self.updateText = nil;
    self.updateURLString = nil;
    self.linkButton = nil;
    self.portalServices = nil;

    self.refreshTimer = nil;

    // TODO make this lazy load
    self.lights = [[NSMutableArray alloc] initWithCapacity:3];
    self.groups = [[NSMutableArray alloc] initWithCapacity:1];
    self.schedules = [[NSMutableArray alloc] init];
    self.users = [[NSMutableArray alloc] initWithCapacity:1];
}

- (void)authorize
{
    NSLog(@"Beginning Hue Bridge Authorization for %@", self.uniqueID);

    LBJSONSource *source = [[LBJSONSource alloc] init];
    source.finishBlock = ^(NSArray *response, NSError *error) {
        if (error) {
            // TODO: Handle an error
            NSLog(@"Got Error");
        }
        else {
            NSLog(@"Response from Bridge: %@", response);
            for (NSDictionary *result in response) {
                if (result[@"error"]) {
                    switch ([result[@"error"][@"type"] integerValue]) {
                        case 101:
                            // Button not pressed
                            [self.authorizationDelegate authorizationDidFail:self canRetry:YES];
                            break;

                        default:
                            break;
                    }
                }
                else if (result[@"success"]) {
                    self.username = result[@"success"][@"username"];
                    [self.authorizationDelegate authorizedBridge:self withToken:self.username];
                }
            }
        }
    };

    NSDictionary *requestData = @{@"username" : @"LightBeeDev", @"devicetype" : @"LightBee"};

    [source postDictionary:requestData withURL:self.apiBaseURL];
}

- (NSURL *)apiBaseURL
{
    return [NSURL URLWithString:[NSString stringWithFormat:@"http://%@/api/", self.ipAddress]];
}

- (NSURL *)apiBaseAuthenticatedURL
{
    return [NSURL URLWithString:[NSString stringWithFormat:@"http://%@/api/%@/", self.ipAddress, self.username]];
}

- (NSString *)description
{
    NSString *format = @"(Bridge '%@' LGSU:%d %d %d %d)";
    return [NSString stringWithFormat:format, self.name, [self.lights count], [self.groups count], [self.schedules count], [self.users count]];
}

#pragma mark - Bridge Operations

- (void)readBridge
{
    NSLog(@"Reading configuration from bridge...");

    LBJSONSource *source = [[LBJSONSource alloc] init];
    source.finishBlock = ^(NSArray *response, NSError *error) {
        NSLog(@"Read configuration from bridge: %@", response);
        if (error) {
            NSLog(@"readBridge: Error in finish block");
            [self handleReadError:error];
        }
        else {
            for (NSDictionary *result in response) {
                if (result[@"error"]) {
                    // TODO handle error
                    NSLog(@"Error response: %@", result[@"error"]);
                }
                if (result[@"lights"]) {
                    [self updateLightsWithDictionary:result[@"lights"]];
                }
                if (result[@"groups"]) {
                    [self updateGroupsWithDictionary:result[@"groups"]];
                }
                if (result[@"config"]) {
                    [self updateConfigWithDictionary:result[@"config"]];
                }
                if (result[@"schedules"]) {
                    [self updateSchedulesWithDictionary:result[@"schedules"]];
                }
            }

            self.lastRefresh = [NSDate date];
        }

        if ([self.delegate respondsToSelector:@selector(bridgeDidFinishReading:)]) {
            [self.delegate bridgeDidFinishReading:self];
        }
    };

    [source getWithURL:self.apiBaseAuthenticatedURL];
}

- (void)handleReadError:(NSError *)error
{
    if ([[error domain] isEqualToString:@"NSURLErrorDomain"]) {
        NSLog(@"%@ %@", [error userInfo][@"NSLocalizedDescriptionKey"],
              [error userInfo][@"NSURLErrorFailingURLStringErrorKey"]);
    }
    else {
        NSLog(@"handleReadError: Unhandled error: %@", error);
    }
}

- (void)readAll
{
    [self readGroupWithIndexKey:@"0"];
    [self readBridge];
}

- (void)autoRefresh:(NSTimer *)timer
{
    NSLog(@"Tick");
    if (self.authorized) {
        [self readAll];
    }
}

#pragma mark - Group Operations

- (void)readGroupWithIndexKey:(NSString *)indexKey
{
    NSLog(@"Reading Group %@ from bridge...", indexKey);

    LBJSONSource *source = [[LBJSONSource alloc] init];
    source.finishBlock = ^(NSArray *response, NSError *error) {
        NSLog(@"Read Group %@ from bridge: %@", indexKey, response);
        if (error) {
            NSLog(@"readGroupWithIndexKey:%@ Error in finish block", indexKey);
            [self handleReadError:error];
        }
        else {
            for (NSDictionary *result in response) {
                if (result[@"error"]) {
                    // TODO handle an API error
                    NSLog(@"Error response from bridge reading group %@", indexKey);
                }

                LBGroup *newGroup = [[LBGroup alloc] initWithDictionary:result forKey:indexKey onBridge:self];
                LBGroup *oldGroup;
                NSUInteger exIndex;
                if ((oldGroup = [self groupWithIndexKey:indexKey index:&exIndex])) {
                    [self.groups replaceObjectAtIndex:exIndex withObject:newGroup];
                }
                else {
                    [self.groups addObject:newGroup];
                }
            }
        }

        if ([self.delegate respondsToSelector:@selector(bridge:didFinishReadingGroupWithIndexKey:)]) {
            [self.delegate bridge:self didFinishReadingGroupWithIndexKey:indexKey];
        }
    };

    NSString *apiPath = [@"groups" stringByAppendingPathComponent:indexKey];
    [source getWithURL:[self.apiBaseAuthenticatedURL URLByAppendingPathComponent:apiPath]];
}

- (LBGroup *)createGroupNamed:(NSString *)name withLights:(NSArray *)lights
{
    LBJSONSource *source = [[LBJSONSource alloc] init];
    NSArray *response = nil;
    NSError *error = nil;
    LBGroup *newGroup = nil;

    // Extract light indexKeys to an array
    NSMutableArray *lightIndexKeys = [[NSMutableArray alloc] initWithCapacity:[lights count]];
    [lights enumerateObjectsUsingBlock:^(LBLight *light, NSUInteger idx, BOOL *stop) {
        [lightIndexKeys addObject:light.indexKey];
    }];

    // POST the request
    NSDictionary *postDict = @{@"name":name,@"lights":lightIndexKeys};
    response = [source synchronousPostDictionary:postDict withURL:[self.apiBaseAuthenticatedURL URLByAppendingPathComponent:@"groups"] error:error];

    // Got an error posting request
    if (error) {
        NSLog(@"Got URL error creating group: %@ %@", error.localizedDescription, error.userInfo);
    }

    // Process response
    if (response) {
        for (NSDictionary *result in response) {
            if (result[@"success"]) {
                // new indexKey in N => @{"id" : "/groups/N"}
                NSString *indexKey = [result[@"success"][@"id"] lastPathComponent];

                // Create the new group and add to the group array
                newGroup = [[LBGroup alloc] initWithDictionary:postDict forKey:indexKey onBridge:self];
                [self.groups addObject:newGroup];

                NSLog(@"Created new group %@ with indexKey %@", name, indexKey);
            }
            if (result[@"error"]) {
                NSLog(@"Got an error response from the bridge creating group: %@", result);
            }
        }
    }

    [AppDelegate saveState];

    return newGroup;
}

- (BOOL)writeGroup:(LBGroup *)group
{
    LBJSONSource *source = [[LBJSONSource alloc] init];
    NSArray *response = nil;
    NSError *error = nil;

    NSDictionary *putDict = @{@"name":group.name,@"lights":group.lightIndexes};
    NSString *apiPath = [@"groups" stringByAppendingPathComponent:group.indexKey];

    response = [source synchronousPutDictionary:putDict withURL:[self.apiBaseAuthenticatedURL URLByAppendingPathComponent:apiPath] error:error];

    // Process response
    if (response) {
        for (NSDictionary *result in response) {
            if (result[@"success"]) {
                // Swap the old group for this new one
                NSUInteger oldIndex;
                [self groupWithIndexKey:group.indexKey index:&oldIndex];
                [self.groups replaceObjectAtIndex:oldIndex withObject:group];
                [AppDelegate saveState];
                return YES;
            }
        }
    }

    return NO;
}

- (BOOL)deleteGroup:(LBGroup *)group
{
    LBJSONSource *source = [[LBJSONSource alloc] init];
    NSArray *response = nil;
    NSError *error = nil;

    NSString *apiPath = [@"groups" stringByAppendingPathComponent:group.indexKey];

    response = [source synchronousDeleteWithURL:[self.apiBaseAuthenticatedURL URLByAppendingPathComponent:apiPath] error:error];

    // Process response
    if (response) {
        for (NSDictionary *result in response) {
            if (result[@"success"]) {
                [self.groups removeObject:group];
                [AppDelegate saveState];
                return YES;
            }
        }
    }

    return NO;
}

- (NSMutableArray *)groups
{
    [_groups sortUsingComparator:[LBGroup indexKeyAscending]];
    return _groups;
}

- (NSMutableArray *)lights
{
    [_lights sortUsingComparator:[LBLight indexKeyAscending]];
    return _lights;
}

#pragma mark - LBTargetActionProtocol

- (void)executeTargetAction:(LBTargetAction *)targetAction
{
    // Rate Limit?
    if (![self rateLimitTargetAction]) {
        LBJSONSource *source = [[LBJSONSource alloc] init];
        source.finishBlock = ^(NSArray *response, NSError *error) {
            NSLog(@"Target Action Completed: %@", response);
        };

        NSURL *actionURL = [self.apiBaseAuthenticatedURL URLByAppendingPathComponent:targetAction.actionPath];
        [source putDictionary:targetAction.action withURL:actionURL];
    }
    else {
        NSLog(@"Target action cancelled: rate limit");
    }
}

//
// YES if this method called less than MAX_ACTION_HZ seconds ago, NO otherwise
//
- (BOOL)rateLimitTargetAction
{
    static NSDate *lastActionTime = nil;

    if (! lastActionTime) {
        lastActionTime = [NSDate distantPast];
    }

    NSDate *nowTime = [NSDate date];
    NSTimeInterval interval = 0.0;

    // Is the window open?
    if ((interval = [nowTime timeIntervalSinceDate:lastActionTime] < MAX_ACTION_HZ)) {
        NSLog(@"TA Windows Closed. Throttling: %0.4f", interval);
        return YES;
    }

    lastActionTime = nowTime;
    NSLog(@"TA Window Open");
    return NO;
}

#pragma mark - Cache Update Processing

- (void)updateConfigWithDictionary:(NSDictionary *)dict
{
//    NSLog(@"Updating bridge configuration cache");

    self.name = dict[@"name"];
    self.macAddress = dict[@"mac"];
    self.dhcp = dict[@"dhcp"];
    self.netmask = dict[@"netmask"];
    self.gatewayAddress = dict[@"gateway"];
    self.proxyAddress = dict[@"proxyaddress"];
    self.proxyPort = dict[@"proxyport"];
    self.softwareVersion = dict[@"swversion"];
    self.linkButton = dict[@"linkbutton"];
    self.portalServices = dict[@"portalservices"];

    NSDictionary *update = dict[@"swupdate"];
    self.updateState = update[@"updatestate"];
    self.updateURLString = update[@"updateurl"];
    self.updateText = update[@"text"];
    self.updateNotify = update[@"notify"];

    [self updateUsersWithDictionary:dict[@"whitelist"]];
}

- (void)updateUsersWithDictionary:(NSDictionary *)dict
{
//    NSLog(@"Updating Users cache...");

    //
    // Prepare to add new and replace existing users
    //
    NSMutableIndexSet *usersToReplaceIndexes = [[NSMutableIndexSet alloc] init];
    NSMutableArray *usersToReplace = [[NSMutableArray alloc] init];
    NSMutableArray *usersToAdd = [[NSMutableArray alloc] init];

    [dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
        LBUser *user = [[LBUser alloc] initWithDictionary:obj forKey:key onBridge:self];
        __block BOOL exists = NO;

        [self.users enumerateObjectsUsingBlock:^(LBUser *existing, NSUInteger idx, BOOL *stop) {
            if ([user.indexKey isEqualToString:existing.indexKey]) {
                [usersToReplace addObject:user];
                [usersToReplaceIndexes addIndex:idx];
                exists = YES;
                *stop = YES;
            }
        }];

        if (exists == NO) {
            // Didn't find it, add
            [usersToAdd addObject:user];
        }
    }];

    //
    // Prepare to remove any lights which aren't in the dictionary
    //
    NSMutableIndexSet *usersToRemoveIndexes = [[NSMutableIndexSet alloc] init];
    NSSet *keys = [NSSet setWithArray:[dict allKeys]];

    [self.users enumerateObjectsUsingBlock:^(LBUser *user, NSUInteger idx, BOOL *stop) {
        if (![keys containsObject:user.indexKey]) {
            [usersToRemoveIndexes addIndex:idx];
        }
    }];

    //
    // Execute prepared changes
    //
    if([usersToAdd count] > 0) {
        [self.users addObjectsFromArray:usersToAdd];
//        NSLog(@"Added %d Users to cache", [usersToAdd count]);
    }

    if ([usersToReplace count] > 0) {
        [self.users replaceObjectsAtIndexes:usersToReplaceIndexes withObjects:usersToReplace];
//        NSLog(@"Replaced %d Users in cache", [usersToReplace count]);
    }

    if ([usersToRemoveIndexes count] > 0) {
        [self.users removeObjectsAtIndexes:usersToRemoveIndexes];
//        NSLog(@"Removed %d Users from cache", [usersToRemoveIndexes count]);
    }
}

//
// Update lights from a dictionary provided by the bridge.
//
- (void)updateLightsWithDictionary:(NSDictionary *)dict
{
//    NSLog(@"Updating Lights cache...");

    //
    // Prepare to add new and replace existing lights
    //
    NSMutableIndexSet *lightsToReplaceIndexes = [[NSMutableIndexSet alloc] init];
    NSMutableArray *lightsToReplace = [[NSMutableArray alloc] init];
    NSMutableArray *lightsToAdd = [[NSMutableArray alloc] init];

    [dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
        LBLight *light = [[LBLight alloc] initWithDictionary:obj forKey:key onBridge:self];
        __block BOOL exists = NO;

        [self.lights enumerateObjectsUsingBlock:^(LBLight *existing, NSUInteger idx, BOOL *stop) {
            if ([light.indexKey isEqualToString:existing.indexKey]) {
                [lightsToReplace addObject:light];
                [lightsToReplaceIndexes addIndex:idx];
                exists = YES;
                *stop = YES;
            }
        }];

        if (exists == NO) {
            // Didn't find it, add
            [lightsToAdd addObject:light];
        }
    }];

    //
    // Prepare to remove any lights which aren't in the dictionary
    //
    NSMutableIndexSet *lightsToRemoveIndexes = [[NSMutableIndexSet alloc] init];
    NSSet *keys = [NSSet setWithArray:[dict allKeys]];

    [self.lights enumerateObjectsUsingBlock:^(LBLight *light, NSUInteger idx, BOOL *stop) {
        if (![keys containsObject:light.indexKey]) {
            [lightsToRemoveIndexes addIndex:idx];
        }
    }];

    //
    // Execute prepared changes
    //
    if([lightsToAdd count] > 0) {
        [self.lights addObjectsFromArray:lightsToAdd];
//        NSLog(@"Added %d Lights to cache", [lightsToAdd count]);
    }

    if ([lightsToReplace count] > 0) {
        [self.lights replaceObjectsAtIndexes:lightsToReplaceIndexes withObjects:lightsToReplace];
//        NSLog(@"Replaced %d Lights in cache", [lightsToReplace count]);
    }

    if ([lightsToRemoveIndexes count] > 0) {
        [self.lights removeObjectsAtIndexes:lightsToRemoveIndexes];
//        NSLog(@"Removed %d Lights from cache", [lightsToRemoveIndexes count]);
    }
}

- (void)updateGroupsWithDictionary:(NSDictionary *)dict
{
//    NSLog(@"Updating Groups cache...");

    //
    // Prepare to add new and replace existing lights
    //
    NSMutableIndexSet *groupsToReplaceIndexes = [[NSMutableIndexSet alloc] init];
    NSMutableArray *groupsToReplace = [[NSMutableArray alloc] init];
    NSMutableArray *groupsToAdd = [[NSMutableArray alloc] init];

    [dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
        LBGroup *group = [[LBGroup alloc] initWithDictionary:obj forKey:key onBridge:self];
        __block BOOL exists = NO;

        [self.groups enumerateObjectsUsingBlock:^(LBGroup *existing, NSUInteger idx, BOOL *stop) {
            if ([group.indexKey isEqualToString:existing.indexKey]) {
                [groupsToReplace addObject:group];
                [groupsToReplaceIndexes addIndex:idx];
                exists = YES;
                *stop = YES;
            }
        }];

        if (exists == NO) {
            // Didn't find it, add
            [groupsToAdd addObject:group];
        }
    }];

    //
    // Prepare to remove any lights which aren't in the dictionary
    //
    NSMutableIndexSet *groupsToRemoveIndexes = [[NSMutableIndexSet alloc] init];
    NSSet *keys = [NSSet setWithArray:[dict allKeys]];

    [self.groups enumerateObjectsUsingBlock:^(LBGroup *group, NSUInteger idx, BOOL *stop) {
        // Dont remove hidden group "0"
        if (![group.indexKey isEqualToString:@"0"] && ![keys containsObject:group.indexKey]) {
            [groupsToRemoveIndexes addIndex:idx];
        }
    }];

    //
    // Execute prepared changes
    //
    if([groupsToAdd count] > 0) {
        [self.groups addObjectsFromArray:groupsToAdd];
//        NSLog(@"Added %d Groups to cache", [groupsToAdd count]);
    }

    if ([groupsToReplace count] > 0) {
        [self.groups replaceObjectsAtIndexes:groupsToReplaceIndexes withObjects:groupsToReplace];
//        NSLog(@"Replaced %d Groups in cache", [groupsToReplace count]);
    }

    if ([groupsToRemoveIndexes count] > 0) {
        [self.groups removeObjectsAtIndexes:groupsToRemoveIndexes];
//        NSLog(@"Removed %d Groups from cache", [groupsToRemoveIndexes count]);
    }
}

- (void)updateSchedulesWithDictionary:(NSDictionary *)dict
{
    // TODO implement schedules API
//    NSLog(@"Schedules not implemented");
}

#pragma mark - Collection Helpers

- (LBUser *)userWithIndexKey:(NSString *)indexKey index:(NSUInteger *)index
{
    return [self collectionItemWithIndexKey:indexKey inCollection:self.users index:index];
}

- (LBLight *)lightWithIndexKey:(NSString *)indexKey index:(NSUInteger *)index
{
    return [self collectionItemWithIndexKey:indexKey inCollection:self.lights index:index];
}

- (LBGroup *)groupWithIndexKey:(NSString *)indexKey index:(NSUInteger *)index
{
    return [self collectionItemWithIndexKey:indexKey inCollection:self.groups index:index];
}

- (id)collectionItemWithIndexKey:(NSString *)indexKey inCollection:(NSArray *)collection index:(NSUInteger *)index
{
    __block id item = nil;
    __block NSUInteger itemIndex;

    [collection enumerateObjectsUsingBlock:^(LBCollectionItem *obj, NSUInteger idx, BOOL *stop) {
        if ([indexKey isEqualToString:obj.indexKey]) {
            item = obj;
            itemIndex = idx;
            *stop = YES;
        }
    }];

    if (index) {
        *index = itemIndex;
    }
    return item;
}

#pragma mark - NSObject

// Two bridges are equal if they share the same unique id (MAC address)
- (BOOL)isEqual:(id)object
{
    if ([object isKindOfClass:[LBBridge class]]) {
        LBBridge *bridge = (LBBridge *)object;
        return (self.uniqueID.hash == bridge.uniqueID.hash);
    }
    return NO;
}

- (NSUInteger)hash
{
    return self.uniqueID.hash;
}

#pragma mark - NSCoding

- (id)initWithCoder:(NSCoder *)decoder
{
    NSString *uid = [decoder decodeObjectForKey:@"uniqueID"];
    NSString *ipAddress = [decoder decodeObjectForKey:@"ipAddress"];

    self = [self initWithUniqueID:uid ipAddress:ipAddress];

    if (self) {
        self.username = [decoder decodeObjectForKey:@"username"];
        self.authorized = [decoder decodeBoolForKey:@"authorized"];
        self.active = [decoder decodeBoolForKey:@"active"];
        self.name = [decoder decodeObjectForKey:@"name"];

        NSLog(@"Decoded Bridge %@", self);
    }

    return self;
}

- (void)encodeWithCoder:(NSCoder *)encoder
{
    [encoder encodeObject:self.uniqueID forKey:@"uniqueID"];
    [encoder encodeObject:self.ipAddress forKey:@"ipAddress"];
    [encoder encodeObject:self.username forKey:@"username"];
    [encoder encodeBool:self.authorized forKey:@"authorized"];
    [encoder encodeBool:self.active forKey:@"active"];
    [encoder encodeObject:self.name forKey:@"name"];
}

#pragma mark - NSKeyValueCoding (Lights)

- (NSUInteger)countOfLights
{
    return [self.lights count];
}

- (id)objectInLightsAtIndex:(NSUInteger)index
{
    return [self.lights objectAtIndex:index];
}

- (NSArray *)lightsAtIndexes:(NSIndexSet *)indexes
{
    return [self.lights objectsAtIndexes:indexes];
}

- (void)getLights:(LBLight * __unsafe_unretained *)buffer range:(NSRange)inRange
{
    [self.lights getObjects:buffer range:inRange];
}

- (void)insertObject:(LBLight *)light inLightsAtIndex:(NSUInteger)index
{
    [self.lights insertObject:light atIndex:index];
}

- (void)insertLights:(NSArray *)lights atIndexes:(NSIndexSet *)indexes
{
    [self.lights insertObjects:lights atIndexes:indexes];
}

- (void)removeObjectFromLightsAtIndex:(NSUInteger)index
{
    [self.lights removeObjectAtIndex:index];
}

- (void)removeLightsAtIndexes:(NSIndexSet *)indexes
{
    [self.lights removeObjectsAtIndexes:indexes];
}

- (void)replaceObjectInLightsAtIndex:(NSUInteger)index withObject:(LBLight *)light
{
    [self.lights replaceObjectAtIndex:index withObject:light];
}

- (void)replaceLightsAtIndexes:(NSIndexSet *)indexes withLights:(NSArray *)lights
{
    [self.lights replaceObjectsAtIndexes:indexes withObjects:lights];
}

#pragma mark - NSKeyValueCoding (Groups)

- (NSUInteger)countOfGroups
{
    return [self.groups count];
}

- (id)objectInGroupsAtIndex:(NSUInteger)index
{
    return [self.groups objectAtIndex:index];
}

- (NSArray *)groupsAtIndexes:(NSIndexSet *)indexes
{
    return [self.groups objectsAtIndexes:indexes];
}

- (void)getGroups:(LBGroup * __unsafe_unretained *)buffer range:(NSRange)inRange
{
    [self.groups getObjects:buffer range:inRange];
}

- (void)insertObject:(LBGroup *)group inGroupsAtIndex:(NSUInteger)index
{
    [self.groups insertObject:group atIndex:index];
}

- (void)insertGroups:(NSArray *)groups atIndexes:(NSIndexSet *)indexes
{
    [self.groups insertObjects:groups atIndexes:indexes];
}

- (void)removeObjectFromGroupsAtIndex:(NSUInteger)index
{
    [self.groups removeObjectAtIndex:index];
}
- (void)removeGroupsAtIndexes:(NSIndexSet *)indexes
{
    [self.groups removeObjectsAtIndexes:indexes];
}
- (void)replaceObjectInGroupsAtIndex:(NSUInteger)index withObject:(LBGroup *)group
{
    [self.groups replaceObjectAtIndex:index withObject:group];
}
- (void)replaceGroupsAtIndexes:(NSIndexSet *)indexes withGroups:(NSArray *)groups
{
    [self.groups replaceObjectsAtIndexes:indexes withObjects:groups];
}

@end


text:
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
  <string name="app_name">СМС бекап+</string>

  <string name="app_description">
     Користите Gmail за чување ваших СМС порука. Ово је побољшана верзија апликације "SMS Backup"
     Кристофа Стадера која подржава враћање СМС порука на телефон. У потпуности је
     уназад компатибилна са апликацијом СМС бекап.
  </string>

  <string name="ui_sync_button_label_idle">Копирај</string>
  <string name="ui_sync_button_label_canceling">Заустављање</string>
  <string name="ui_sync_button_label_syncing">Заустави</string>

  <string name="ui_restore_button_label_idle">Врати</string>
  <string name="ui_restore_button_label_restoring">Заустави</string>

  <string name="status_idle">Стање мировања</string>
  <string name="status_idle_details">Последње копирање: <xliff:g id="last_backup_date">%1$s</xliff:g></string>
  <string name="status_idle_details_never">никад</string>

  <string name="status_working">Радим</string>

  <string name="status_backup">Прављење резервне копије</string>
  <string name="status_restore">Враћање</string>

  <string name="status_backup_details">Копирано <xliff:g id="backed_up_items">%1$d</xliff:g>/<xliff:g id="total_items">%2$d</xliff:g> ставки\u2026</string>
  <string name="status_restore_details">Враћено <xliff:g id="backed_up_items">%1$d</xliff:g>/<xliff:g id="total_items">%2$d</xliff:g> ставки\u2026</string>
  <string name="status_login_details">Пријављивање\u2026</string>
  <string name="status_updating_threads">Ажурирање преписки\u2026</string>
  <string name="status_done">Завршено</string>
  <string name="status_backup_done_details_max_per_sync">Достигнут је максимум од %1$d ставки по резервној копији. Поново покрените прављење резервне копије за наставак.</string>
  <string name="status_backup_done_details_noitems">Нема ставки за резервно копирање.</string>

  <plurals name="status_backup_done_details">
    <item quantity="one">Успешно је копирана <xliff:g id="total_items">%1$d</xliff:g> ставка.</item>
    <item quantity="other">Успешно је копирано <xliff:g id="total_items">%1$d</xliff:g> ставки.</item>
  </plurals>

  <plurals name="status_restore_done_details">
    <item quantity="one">Успешно је враћена <xliff:g id="total_items">%1$d</xliff:g> ставка (%2$d дуп.).</item>
    <item quantity="other">Успешно је враћено <xliff:g id="total_items">%1$d</xliff:g> ставки (%2$d дуп.).</item>
  </plurals>

  <string name="status_restore_canceled_details">%1$d/%2$d ставки успешно враћено.</string>

  <string name="status_unknown_error">Грешка</string>
  <string name="notification_general_error">СМС бекап+ грешка</string>

  <string name="status_unknown_error_details">Грешка током прављења/враћања копије:\n<xliff:g id="error_description">%1$s</xliff:g></string>
  <string name="status_auth_failure">Грешка пријаве</string>
  <string name="notification_auth_failure">СМС бекап+ грешка пријаве</string>

  <string name="status_auth_failure_details_xoauth">Грешка XOAuth овлашћења. Уверите се да сте омогућили ИМАП у подешавањима Gmail налога.</string>
  <string name="status_auth_failure_details_plain">Грешка ИМАП овлашћења.
  Уверите се да су корисничко име и лозинка исправни.</string>
  <string name="status_calc_details">Рачунање\u2026</string>
  <string name="status_canceled">Отказано</string>
  <string name="status_canceled_details">Успешно је направљена резервна копија за %1$d/%2$d ставки.</string>
  <string name="status_gmail_temp_error">Привремена Gmail ИМАП грешка, покушајте касније.</string>

  <string name="ui_settings_advanced_label">Напредне поставке</string>
  <string name="ui_settings_advanced_desc">Уредите напредне поставке.</string>

  <string name="imap_settings">Поставке ИМАП-а</string>
  <string name="ui_login_label">Корисничко име</string>
  <string name="ui_login_desc">Ваш ИМАП налог/е-адреса.</string>
  <string name="ui_password_label">Лозинка</string>
  <string name="ui_password_desc">Лозинка вашег ИМАП налога. Користи се само када је XOAuth онемогућен.</string>
  <string name="ui_password_dialog_msg">Лозинка је сачувана на овом уређају и користи се само за пријаву.</string>
  <string name="ui_server_label">Адреса сервера</string>
  <string name="ui_server_desc">Адреса сервера са којим желите да се синхронизујете, укључујући и порт.</string>
  <string name="ui_protocol_label">Безбедност</string>
  <string name="ui_authentication_label">Аутентификација</string>
  <string name="plain">Обичан текст</string>

  <string name="ui_imap_folder_label">Етикета за СМС</string>
  <string name="ui_imap_folder_label_dialog_msg">Копиране СМС поруке ће бити означене овом етикетом у Gmail сандучету.</string>
  <string name="ui_mail_subject_prefix_label">Префикс предмета е-поште</string>
  <string name="ui_mail_subject_prefix_desc">Користи име ИМАП фасцикле за префикс у предмету е-поште</string>

  <string name="ui_max_items_per_sync_label">Ставки по копији</string>
  <string name="ui_max_items_per_sync_desc">Максималан број ставки по резервној копији.</string>
  <string name="ui_max_items_per_restore_label">Ставки по враћању</string>
  <string name="ui_max_items_per_restore_desc">Максималан број ставки по враћању резерве.</string>

  <string name="ui_restore_starred_only_label">Ставке са звездицом</string>
  <string name="ui_restore_starred_only_desc">Врати само ставке са звездицом.</string>

  <string name="ui_backup_mms_label">Копирај ММС</string>
  <string name="ui_backup_mms_desc">Омогући копирање ММС порука</string>

  <string name="ui_backup_calllog_label">Копирај евид. позива</string>
  <string name="ui_backup_calllog_desc">Омогући копирање евиденције позива</string>

    <string name="ui_backup_calllog_sync_calendar_enabled_label">Синхронизуј календар</string>
  <string name="ui_backup_calllog_sync_calendar_enabled_desc">Додај евиденцију позива у календар</string>

  <string name="ui_backup_calllog_types_label">Врсте позива</string>
  <string name="ui_backup_calllog_types_desc">Које врсте позива да копирам</string>

  <string name="ui_backup_calllog_sync_calendar_label">Ниједан</string>
  <string name="ui_backup_calllog_sync_calendar_desc">Календар се користи за евиденцију позива</string>

  <string name="ui_imap_folder_calllog_label">Етикета за евиденцију позива</string>
  <string name="ui_imap_folder_calllog_label_dialog_msg">Копирана евиденција позива ће бити означена овом етикетом у Gmail сандучету.</string>

  <string name="ui_mark_as_read_label">Означи пошту прочитаном</string>
  <string name="ui_mark_as_read_desc">Означи поруке у сандучету као прочитане.</string>

  <string name="mark_as_read_always_read">Увек означи прочитаним</string>
  <string name="mark_as_read_always_unread">Увек означи непрочитаним</string>
  <string name="mark_as_read_message_status">Користи стање поруке</string>

  <string name="ui_mark_as_read_restore_label">Означи СМС прочитаним</string>
  <string name="ui_mark_as_read_restore_desc">>Означи све враћене СМС поруке као прочитане.</string>

  <string name="ui_email_address_style_label">Стил адресе е-поште</string>
  <string name="ui_email_address_style_desc">Формат адреса е-поште</string>

  <string name="ui_enable_auto_sync_label">Аутоматска резерва</string>
  <string name="ui_enable_auto_sync_desc">Аутоматски прави резервну копију нових СМС порука.</string>
  <string name="ui_enable_auto_sync_summary">Аутоматски копирај %1$s.</string>
  <string name="ui_enable_auto_sync_no_enabled_summary">Није одређен тип ставки за резерву.</string>

  <string name="sd_card_disclaimer">Аутоматско прављење резервне копије можда неће радити поуздано ако је апликација инсталирана на СД картици.</string>

  <string name="ui_auto_backup_schedule_label">Редован распоред</string>
  <string name="ui_auto_backup_schedule_desc">Када копирати СМС поруке</string>

  <string name="ui_auto_backup_incoming_schedule_label">Распоред за долазне</string>
  <string name="ui_auto_backup_incoming_schedule_desc">Када копирати долазне СМС поруке (по пристизању)</string>

  <string name="ui_wifi_only_label">Захтевај бежични</string>
  <string name="ui_wifi_only_desc">Копирај само при вези на бежични</string>

  <string name="ui_notifications_label">Обавештења</string>
  <string name="ui_notifications_desc">Обавештења у позадини</string>

  <string name="error_wifi_only_no_connection">Нема везе са бежичним</string>
  <string name="error_no_connection">Нема везе са интернетом</string>
  <string name="error_sms_provider_not_writable">Не могу писати СМС оператеру</string>

  <string name="ui_dialog_missing_credentials_title">Подаци за пријаву</string>

    <string name="ui_dialog_missing_credentials_msg_plain">Најпре морате да да поставите
  ваше корисничко име и лозинку.</string>

  <string name="ui_dialog_invalid_imap_folder_title">Неисправно име етикете</string>
  <string name="ui_dialog_invalid_imap_folder_msg">Име етикете не сме да садржи размаке (на почетку или на крају).</string>
  <string name="ui_dialog_first_sync_title">Прва резерва</string>
  <string name="ui_dialog_first_sync_msg">Копирати или прескочити све поруке тренутно смештене на овом уређају?</string>
  <string name="ui_dialog_first_sync_msg_batched">Копирати или прескочити све поруке тренутно смештене на овом уређају?\n\nНапомена: Резервна копија ће бити подељена у гране са по %1$d порука.</string>
  <string name="ui_dialog_access_token_msg">Захтевам приступ…</string>

  <string name="ui_dialog_access_token_error_title">Неуспех овлашћења</string>
  <string name="ui_dialog_access_token_error_msg">Нисам добио токен за приступ. Уверите се да сте изабрали
    "Дозволи приступ".
  </string>

  <string name="ui_dialog_connect_msg">Отвориће се прозор прегледача који ће тражити од вас да се пријавите на Gmail и дозволите %1$s да приступи вашој е-пошти.</string>
  <string name="ui_dialog_disconnect_msg">Желите ли сигурно? Биће потребна поновно овлашћење вашег налога након што прекинете везу, а локална синхронизација биће поништена.</string>

  <!--<string name="ui_dialog_upgrade_title">Подаци о надоградњи</string>-->

  <string name="ui_sync">Копирај</string>
  <string name="ui_skip">Прескочи</string>
  <string name="err_sync_requires_login_info">Не могу да направим резервну копију без података о пријави.</string>
  <string name="menu_info">О програму</string>
  <string name="menu_reset">Поништи</string>
  <string name="ui_dialog_reset_title">Поништавање трен. стања синхрон.</string>
  <string name="ui_dialog_reset_message">Желите ли да поништите тренутно стање
  синхронизације? И даље касније можете да изаберете да прескочите све поруке.</string>
  <string name="all_messages">Све</string>

  <string name="gmail_needs_connecting">Тапните да бисте се повезали са вашим Gmail налогом (потребно је).</string>
  <string name="gmail_already_connected">Повезан као %1$s.</string>

  <string name="ui_connected_label">Повежи</string>

  <string name="call_incoming">долазни позиви</string>
  <string name="call_outgoing">одлазни позиви</string>
  <string name="call_missed">пропуштени позиви</string>

  <string name="call_missed_text">Пропуштен позив од %1$s</string>
  <string name="call_incoming_text">Позив од %1$s</string>
  <string name="call_outgoing_text">Позван/а %1$s</string>

  <string name="call_number_field">Број: %1$s</string>
  <string name="call_duration_field">Трајање: %1$s</string>

  <string name="sms_with_field">Преписка са %1$s</string>
  <string name="call_with_field">Разговор са %1$s</string>

  <string name="ui_backup_contact_group_label">Контакти за копирање</string>
  <string name="ui_backup_contact_group_desc">Направи резервну копију само ставки из назначене групе</string>

  <string name="everybody">Сви</string>

  <string name="call_type_everything">Сви</string>
  <string name="call_type_incoming">Само долазне позиве</string>
  <string name="call_type_outgoing">Само одлазне позиве</string>
  <string name="call_type_incoming_outgoing">Долазне и одлазне позиве</string>
  <string name="call_type_missed">Само пропуштене позиве</string>

  <string name="email_address_style_name">Име</string>
  <string name="email_address_style_name_and_number">Име (+381 123456)</string>
  <string name="email_address_style_number">+381 123456</string>

  <string name="_1h">1 ч</string>
  <string name="_2h">2 ч</string>
  <string name="_6h">6 ч</string>
  <string name="_24h">24 ч</string>
  <string name="never">Никада</string>

  <string name="_1min">1 мин.</string>
  <string name="_3mins">3 мин.</string>
  <string name="_30mins">30 мин.</string>

  <string name="security_none">Ништа</string>

  <string name="ui_third_party_integration_label">Интеграција других апл.</string>
  <string name="ui_third_party_integration_desc">Дозволи другим апликацијама да започну копирање путем емитовања намере</string>

  <!-- new ui -->
  <string name="ui_backup_settings_label">Поставке копирања</string>
  <!-- <string name="ui_backup_settings_desc">Поставке копирања</string> -->

  <string name="ui_backup_calllog_settings_label">Поставке евиденције позива</string>
  <!-- <string name="ui_backup_calllog_settings_desc">Поставке евиденције позива</string> -->

  <string name="auto_backup_settings">Подеш. аутом. копирања</string>

  <string name="ui_restore_settings_label">Поставке враћања</string>
  <!-- <string name="ui_restore_settings_desc">Поставке враћања</string> -->

  <string name="ui_backup_sms_label">Копирај СМС</string>
  <string name="ui_backup_sms_desc">Омогући копирање СМС порука</string>

  <string name="ui_restore_sms_label">Врати СМС</string>

  <string name="ui_restore_calllog_label">Врати евид. позива</string>

  <string name="sms">СМС</string>
  <string name="mms">ММС</string>
  <string name="calllog">Евиденција позива</string>

  <string name="app_log_skip_backup_already_running">Прескочено, јер је већ покренуто прављење резервне копије</string>
  <string name="app_log_skip_backup_skip_messages">Прескочи све поруке</string>
  <string name="app_log_skip_backup_no_items">Прескочено (нема ставки)</string>
  <string name="app_log_missing_credentials">Прескочено (недостају акредитиви)</string>
  <string name="app_log_backup_requested">Захтевано копирање (%1$s)</string>
  <string name="app_log_start_backup">Почињем копирање (%1$s)</string>
  <string name="app_log_backup_messages">Копирам (%1$d СМС, %2$d ММС, %3$d евид. позива)</string>
  <string name="app_log_backup_messages_with_config">Користим поставку: %1$s</string>
  <string name="app_log_backup_canceled">Копирање отказано</string>
  <string name="app_log_backup_finished">Копирање завршено</string>

  <string name="app_log_backup_failed_authentication">Грешка аутентификације: %1$s</string>
  <string name="app_log_backup_failed_general_error">Општа грешка: %1$s</string>
  <string name="app_log_backup_failed_connectivity">Копирање прескочено, нема везе</string>
  <string name="app_log_scheduled_next_sync">Следеће копирање заказано за: %1$s</string>
  <string name="app_log_no_next_sync">Није заказано следеће копирање</string>

  <string name="menu_view_log">Прикажи дневник</string>
  <string name="app_log_empty">Дневник је празан. Омогућите га у "напредним поставкама".</string>

  <string name="pref_app_log">Дневник синхрон.</string>
  <string name="pref_app_log_enabled">Дневник је смештен на СД картици као "sms_backup_plus.log".</string>
  <string name="pref_app_log_disabled">Онемогућено</string>

  <string name="pref_app_log_debug">Додатни подаци испр. грешака</string>
  <string name="pref_app_log_debug_summary">Омогућује додатне записе у дневнику синхронизације</string>

  <string name="source_incoming">долазно</string>
  <string name="source_regular">регуларно</string>
  <string name="source_3rd_party">остале апл.</string>
  <string name="source_manual">ручно</string>
  <string name="source_unknown">непознато</string>

  <string name="ui_confirm_action_label">Потврда радњи</string>
  <string name="ui_confirm_action_desc">Питај пре извршења копирања или враћања</string>
  <string name="ui_dialog_confirm_action_title">Потврда радње</string>
  <string name="ui_dialog_confirm_action_msg">Желите ли заиста да извршите ову радњу?</string>

  <string name="ui_dialog_account_manager_token_error">
      Нисам добио токен од система. Желите ли да покушате методу аутентификације помоћу прегледача?
  </string>
  <string name="select_google_account">Изаберите Гугл налог</string>

  <!-- donation related -->
  <string name="donate">Донација</string>
  <string name="donate_summary">Користим безбедно плаћање Гуглове Плеј продавнице.</string>

  <string name="ui_dialog_donate_message">Наведите износ:</string>
  <string name="ui_donation_success_message">Донација је успела, хвала вам!</string>
  <string name="ui_donation_failure_message">Донације није успела: %1$s</string>

  <!-- purchase errors -->
  <string name="donation_error_iab_unavailable">Плаћање преко апликације није доступно</string>
  <string name="donation_error_unavailable">није доступно</string>
  <string name="donation_error_canceled">отказано</string>
  <string name="donation_error_already_owned">већ сте донирали</string>

  <string name="ui_dialog_sms_default_package_change_title">Белешка надоградње</string>
  <string name="ui_dialog_sms_default_package_change_msg">
    Да бисте вратили ваше поруке на Андроид Киткету и Лолипопу,
    морате поставити СМС бекап+ за СМС апликацију током враћања.
    Бићете упитани да вратите изворну поставку након враћања.
  </string>
</resources>

redpajama_set_name: RedPajamaGithub


text:
Can be run directly with the [bitrise CLI](https://github.com/bitrise-io/bitrise),
just `git clone` this repository, `cd` into it's folder in your Terminal/Command Line
and call `bitrise run test`.

*Check the `bitrise.yml` file for required inputs which have to be
added to your `.bitrise.secrets.yml` file!*

## Share your own Step

You can share your Step or step version with the [bitrise CLI](https://github.com/bitrise-io/bitrise). Just run `bitrise share` and follow the guide it prints.


text:
module HttpCapture
  VERSION = File.read(File.join(File.dirname(__FILE__), "../../VERSION")) rescue "0.0.0"
end


text:
//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

namespace CsReverseAjax {

    public partial class Send {

        /// <summary>
        /// form1 control.
        /// </summary>
        /// <remarks>
        /// Auto-generated field.
        /// To modify move field declaration from designer file to code-behind file.
        /// </remarks>
        protected global::System.Web.UI.HtmlControls.HtmlForm form1;

        /// <summary>
        /// lblTo control.
        /// </summary>
        /// <remarks>
        /// Auto-generated field.
        /// To modify move field declaration from designer file to code-behind file.
        /// </remarks>
        protected global::System.Web.UI.WebControls.Label lblTo;

        /// <summary>
        /// txtTo control.
        /// </summary>
        /// <remarks>
        /// Auto-generated field.
        /// To modify move field declaration from designer file to code-behind file.
        /// </remarks>
        protected global::System.Web.UI.WebControls.TextBox txtTo;

        /// <summary>
        /// lblMessage control.
        /// </summary>
        /// <remarks>
        /// Auto-generated field.
        /// To modify move field declaration from designer file to code-behind file.
        /// </remarks>
        protected global::System.Web.UI.WebControls.Label lblMessage;

        /// <summary>
        /// txtMessage control.
        /// </summary>
        /// <remarks>
        /// Auto-generated field.
        /// To modify move field declaration from designer file to code-behind file.
        /// </remarks>
        protected global::System.Web.UI.WebControls.TextBox txtMessage;

        /// <summary>
        /// btnSend control.
        /// </summary>
        /// <remarks>
        /// Auto-generated field.
        /// To modify move field declaration from designer file to code-behind file.
        /// </remarks>
        protected global::System.Web.UI.WebControls.Button btnSend;
    }
}


text:
#ifndef _QMITKTENSORRECONSTRUCTIONVIEW_H_INCLUDED
#define _QMITKTENSORRECONSTRUCTIONVIEW_H_INCLUDED

#include <QmitkFunctionality.h>

#include <string>

#include "ui_QmitkTensorReconstructionViewControls.h"

#include <mitkDiffusionImage.h>
#include <mitkTensorImage.h>

typedef short DiffusionPixelType;

struct TrSelListener;

/*!
 * \ingroup org_mitk_gui_qt_tensorreconstruction_internal
 *
 * \brief QmitkTensorReconstructionView
 *
 * Document your class here.
 *
 * \sa QmitkFunctionality
 */
class QmitkTensorReconstructionView : public QmitkFunctionality
{

  friend struct TrSelListener;

  // this is needed for all Qt objects that should have a MOC object (everything that derives from QObject)
  Q_OBJECT

  public:

  static const std::string VIEW_ID;

  QmitkTensorReconstructionView();
  virtual ~QmitkTensorReconstructionView();

  virtual void CreateQtPartControl(QWidget *parent);

  /// \brief Creation of the connections of main and control widget
  virtual void CreateConnections();

  /// \brief Called when the functionality is activated
  virtual void Activated();

  virtual void Deactivated();

  virtual void StdMultiWidgetAvailable (QmitkStdMultiWidget &stdMultiWidget);
  virtual void StdMultiWidgetNotAvailable();

  static const int nrconvkernels;

protected slots:

  void TensorsToQbi();
  void TensorsToDWI();
  void DoTensorsToDWI(mitk::DataStorage::SetOfObjects::Pointer inImages);
  void Advanced1CheckboxClicked();
  void Reconstruct();
  void ResidualCalculation();
  void ResidualClicked(int slice, int volume);

protected:

  void ItkTensorReconstruction(mitk::DataStorage::SetOfObjects::Pointer inImages);
  void TeemTensorReconstruction(mitk::DataStorage::SetOfObjects::Pointer inImages);
  void TensorReconstructionWithCorr(mitk::DataStorage::SetOfObjects::Pointer inImages);

  void OnSelectionChanged( std::vector<mitk::DataNode*> nodes );

  Ui::QmitkTensorReconstructionViewControls* m_Controls;

  QmitkStdMultiWidget* m_MultiWidget;

  typedef vnl_vector_fixed< double, 3 >                         GradientType;
  typedef itk::VectorContainer< unsigned int, GradientType >    GradientListType;

  template<int ndirs> GradientListType::Pointer MakeGradientList();

  template<int L>
  void TemplatedAnalyticalTensorReconstruction(mitk::DiffusionImage<DiffusionPixelType>* vols,
    float lambda, std::string nodename, std::vector<mitk::DataNode::Pointer>* nodes, int normalization);

  void SetDefaultNodeProperties(mitk::DataNode::Pointer node, std::string name);

  mitk::DataNode::Pointer m_DiffusionImage;
  mitk::DataNode::Pointer m_TensorImage;
  mitk::DataStorage::SetOfObjects::Pointer m_DiffusionImages;
  mitk::DataStorage::SetOfObjects::Pointer m_TensorImages;
};

#endif // _QMITKTENSORRECONSTRUCTIONVIEW_H_INCLUDED


text:
package org.elasticsearch.common.lucene.uid;

import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.LeafReader;
import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.index.Term;
import org.apache.lucene.util.CloseableThreadLocal;
import org.elasticsearch.common.util.concurrent.ConcurrentCollections;

import java.io.IOException;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ConcurrentMap;

/** Utility class to resolve the Lucene doc ID, version, seqNo and primaryTerms for a given uid. */
public final class VersionsAndSeqNoResolver {

    static final ConcurrentMap<IndexReader.CacheKey, CloseableThreadLocal<PerThreadIDVersionAndSeqNoLookup[]>> lookupStates =
        ConcurrentCollections.newConcurrentMapWithAggressiveConcurrency();

    // Evict this reader from lookupStates once it's closed:
    private static final IndexReader.ClosedListener removeLookupState = key -> {
        CloseableThreadLocal<PerThreadIDVersionAndSeqNoLookup[]> ctl = lookupStates.remove(key);
        if (ctl != null) {
            ctl.close();
        }
    };

    private static PerThreadIDVersionAndSeqNoLookup[] getLookupState(IndexReader reader, String uidField) throws IOException {
        // We cache on the top level
        // This means cache entries have a shorter lifetime, maybe as low as 1s with the
        // default refresh interval and a steady indexing rate, but on the other hand it
        // proved to be cheaper than having to perform a CHM and a TL get for every segment.
        // See https://github.com/elastic/elasticsearch/pull/19856.
        IndexReader.CacheHelper cacheHelper = reader.getReaderCacheHelper();
        CloseableThreadLocal<PerThreadIDVersionAndSeqNoLookup[]> ctl = lookupStates.get(cacheHelper.getKey());
        if (ctl == null) {
            // First time we are seeing this reader's core; make a new CTL:
            ctl = new CloseableThreadLocal<>();
            CloseableThreadLocal<PerThreadIDVersionAndSeqNoLookup[]> other = lookupStates.putIfAbsent(cacheHelper.getKey(), ctl);
            if (other == null) {
                // Our CTL won, we must remove it when the reader is closed:
                cacheHelper.addClosedListener(removeLookupState);
            } else {
                // Another thread beat us to it: just use their CTL:
                ctl = other;
            }
        }

        PerThreadIDVersionAndSeqNoLookup[] lookupState = ctl.get();
        if (lookupState == null) {
            lookupState = new PerThreadIDVersionAndSeqNoLookup[reader.leaves().size()];
            for (LeafReaderContext leaf : reader.leaves()) {
                lookupState[leaf.ord] = new PerThreadIDVersionAndSeqNoLookup(leaf.reader(), uidField);
            }
            ctl.set(lookupState);
        }

        if (lookupState.length != reader.leaves().size()) {
            throw new AssertionError("Mismatched numbers of leaves: " + lookupState.length + " != " + reader.leaves().size());
        }

        if (lookupState.length > 0 && Objects.equals(lookupState[0].uidField, uidField) == false) {
            throw new AssertionError("Index does not consistently use the same uid field: ["
                    + uidField + "] != [" + lookupState[0].uidField + "]");
        }

        return lookupState;
    }

    private VersionsAndSeqNoResolver() {
    }

    /** Wraps an {@link LeafReaderContext}, a doc ID relative to the context doc base and a version. */
    public static class DocIdAndVersion {
        public final int docId;
        public final long version;
        public final long seqNo;
        public final long primaryTerm;
        public final LeafReader reader;
        public final int docBase;

        public DocIdAndVersion(int docId, long version, long seqNo, long primaryTerm, LeafReader reader, int docBase) {
            this.docId = docId;
            this.version = version;
            this.seqNo = seqNo;
            this.primaryTerm = primaryTerm;
            this.reader = reader;
            this.docBase = docBase;
        }
    }

    /** Wraps an {@link LeafReaderContext}, a doc ID relative to the context doc base and a seqNo. */
    public static class DocIdAndSeqNo {
        public final int docId;
        public final long seqNo;
        public final LeafReaderContext context;

        DocIdAndSeqNo(int docId, long seqNo, LeafReaderContext context) {
            this.docId = docId;
            this.seqNo = seqNo;
            this.context = context;
        }
    }

    /**
     * Load the internal doc ID and version for the uid from the reader, returning

     *
null if the uid wasn't found,
     *
a doc ID and a version otherwise
     *

     */
    public static DocIdAndVersion loadDocIdAndVersion(IndexReader reader, Term term, boolean loadSeqNo) throws IOException {
        PerThreadIDVersionAndSeqNoLookup[] lookups = getLookupState(reader, term.field());
        List<LeafReaderContext> leaves = reader.leaves();
        // iterate backwards to optimize for the frequently updated documents
        // which are likely to be in the last segments
        for (int i = leaves.size() - 1; i >= 0; i--) {
            final LeafReaderContext leaf = leaves.get(i);
            PerThreadIDVersionAndSeqNoLookup lookup = lookups[leaf.ord];
            DocIdAndVersion result = lookup.lookupVersion(term.bytes(), loadSeqNo, leaf);
            if (result != null) {
                return result;
            }
        }
        return null;
    }

    /**
     * Loads the internal docId and sequence number of the latest copy for a given uid from the provided reader.
     * The result is either null or the live and latest version of the given uid.
     */
    public static DocIdAndSeqNo loadDocIdAndSeqNo(IndexReader reader, Term term) throws IOException {
        final PerThreadIDVersionAndSeqNoLookup[] lookups = getLookupState(reader, term.field());
        final List<LeafReaderContext> leaves = reader.leaves();
        // iterate backwards to optimize for the frequently updated documents
        // which are likely to be in the last segments
        for (int i = leaves.size() - 1; i >= 0; i--) {
            final LeafReaderContext leaf = leaves.get(i);
            final PerThreadIDVersionAndSeqNoLookup lookup = lookups[leaf.ord];
            final DocIdAndSeqNo result = lookup.lookupSeqNo(term.bytes(), leaf);
            if (result != null) {
                return result;
            }
        }
        return null;
    }
}


text:
from gitcd.interface.cli.abstract import BaseCommand
from gitcd.git.branch import Branch

class Finish(BaseCommand):

    def run(self, branch: Branch):
        remote = self.getRemote()

        testBranch = self.config.getTest()
        masterBranch = self.config.getMaster()

        if branch.getName() == masterBranch:
            # maybe i should use recursion here
            # if anyone passes master again, i wouldnt notice
            branch = Branch('%s%s' % (
                branch.getName(),
                self.interface.askFor(
                    "You passed your master branch name as feature branch,\
                    please give a different name."
                )
            ))

        if testBranch:
            if branch.getName().startswith(testBranch):
                # maybe i should use recursion here
                # if anyone passes master again, i wouldnt notice
                branch = Branch('%s%s' % (
                    branch.getName(),
                    self.interface.askFor(
                        "You passed your test branch name as feature branch,\
                        please give a different branch."
                    )
                ))
        self.checkRepository()
        self.checkBranch(remote, branch)

        master = Branch(masterBranch)
        remote.merge(master, branch)

        deleteFeatureBranch = self.interface.askFor(
            "Delete your feature branch?", ["yes", "no"], "yes"
        )

        if deleteFeatureBranch == "yes":
            # delete feature branch remote and locally
            remote.delete(branch)
            branch.delete()


text:
module Indexers
  module Adapter
    class Base
      attr_reader :index_name

      def initialize(index_name:)
        @index_name = index_name
      end
    end
  end
end


text:
package org.codehaus.jparsec;

/**
 * Parses any nestable comment pattern.
 *
 * @author Ben Yu
 */
final class NestableBlockCommentScanner extends Parser<Void> {
  private final Parser<?> openQuote;
  private final Parser<?> closeQuote;
  private final Parser<?> commented;

  NestableBlockCommentScanner(Parser<?> openQuote, Parser<?> closeQuote, Parser<?> commented) {
    this.openQuote = openQuote;
    this.closeQuote = closeQuote;
    this.commented = commented;
  }

  @Override boolean apply(final ParseContext ctxt) {
    if (!openQuote.run(ctxt)) return false;
    for(int level = 1; level > 0;) {
      final int step = ctxt.step;
      final int at = ctxt.at;
      if (closeQuote.run(ctxt)) {
        if (at == ctxt.at) {
          throw new IllegalStateException("closing comment scanner not consuming input.");
        }
        level--;
        continue;
      }
      if (!ParserInternals.stillThere(ctxt, at, step)) return false;
      if (openQuote.run(ctxt)) {
        if (at == ctxt.at) {
          throw new IllegalStateException("opening comment scanner not consuming input.");
        }
        level++;
        continue;
      }
      if (!ParserInternals.stillThere(ctxt, at, step)) return false;
      if (commented.run(ctxt)) {
        if (at == ctxt.at) {
          throw new IllegalStateException("commented scanner not consuming input.");
        }
        continue;
      }
      return false;
    }
    ctxt.result = null;
    return true;
  }

  @Override public String toString() {
    return "nestable block comment";
  }
}


text:
Soccer CLI
=====

[![PyPI version](https://badge.fury.io/py/soccer-cli.svg)](http://badge.fury.io/py/soccer-cli) [![Join the chat at https://gitter.im/architv/soccer-cli](https://badges.gitter.im/architv/soccer-cli.svg)](https://gitter.im/architv/soccer-cli?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)

Soccer for Hackers - a CLI for all the football scores.

![](http://i.imgur.com/9QbcUrj.gif)

Install
=====

An API key from [football-data.org](http://api.football-data.org/) will be required and you can register for one [here](http://api.football-data.org/client/register).

### Build from source

```bash
$ git clone https://github.com/architv/soccer-cli.git
$ cd soccer-cli
$ python setup.py install
```

You can set the API key using an environment variable as shown above or create a file `.soccer-cli.ini` in your home folder (`/home/username/.soccer-cli.ini`) that contains only your API token, such that:

```bash
$ cat /home/username/.soccer-cli.ini
<YOUR_API_TOKEN>
```

#### Note:
Currently supports Linux, Mac OS X, NetBSD, FreeBSD and Windows.

To get colorized terminal output on Windows, make sure to install [ansicon](https://github.com/adoxa/ansicon/releases/latest) and [colorama](https://pypi.org/project/colorama/).

Usage
====

### Get standings for a league

```bash
$ soccer --standings --league=PL # PL is the league code for English Premier League
```

### Get scores for a particular team

```bash
$ soccer --team=MUFC # MUFC is the team code for Manchester United
$ soccer --team=PSG --time=10 # scores for all the Paris Saint-Germain games over the past 10 days
```

### Get upcoming fixtures

```bash
$ soccer --time 5 --upcoming # get upcoming fixtures for next 5 days
$ soccer --time 5 --upcoming --use12hour # upcoming fixture for next 5 days with timings in 12 hour format
```

### Get scores for live games

```bash
$ soccer --live
```

### Get scores for a particular league

```bash
$ soccer --league=BL # BL is the league code for Bundesliga
$ soccer --league=FL --time=15 # get scores for all the French Ligue games over the past 15 days
```

### Get information about players of a team

```bash
$ soccer --team=JUVE --players
```

### Get scores for all seven leagues with a set time period

```bash
$ soccer --time=10 # get scores for all the seven leagues over the past 10 days
```

### Get the output in csv or json

```bash
$ soccer --league PL --standings --csv # prints the output in csv format
$ soccer --league PL --standings --json # prints the output in json format
```

### Store the ouput in a file

```bash
$ soccer --league PL --standings --csv -o 'standings.csv' # stores the ouput in csv format in `standings.csv`
```

### Help
```bash
$ soccer --help
```
### List of supported leagues and their league codes

- World:
  - WC: World Cup (if running/active)
- Europe:
  - CL: Champions League
  - EC: European Championships (if running/active)
- Brazil:
  - BSA: Brazil Serie A
- England:
  - PL: English Premier League
  - ELC: English Championship
- France:
  - FL1: Ligue 1
- Germany:
  - BL: Bundesliga
- Italy:
  - SA: Serie A
- Netherlands:
  - DED: Eredivisie
- Portugal:
  - PPL: Primeira Liga
- Spain:
  - LLIGA: La Liga

### Team and team codes

For a full list of supported team and team codes [see this](soccer/teams.json).

### Tests

To run testing suite from root of repo

```bash
$ python -m unittest discover tests
```

To run specific test file (in this case the tests in test_request_handler.py)

```bash
$ python -m unittest tests.test_request_handler
```

Demo
====

### Standings
![standings](http://i.imgur.com/voyWLQE.gif)

### Live scores
![](http://i.imgur.com/EX9GMAM.gif)

### Team scores
![](http://i.imgur.com/QfvH8QL.png)

### Output in json format
![](http://i.imgur.com/jqGhLia.gif)

Todo
====
- [ ] Enable cache.
- [ ] Add more test cases.
- [x] Add fixtures for UEFA Champions League.
- [ ] Add league filter for live scores.
- [x] Color coding for Europa league and differentiation between straight CL and CL playoff spots, and the same for EL spots.
- [x] Add support for team line up.
- [ ] A built in watch feature so you can run once with --live and just leave the program running.
- [ ] Python 3 support.

Licence
====
Open sourced under [MIT License](LICENSE)

Support
====
If you like my work, please support the project by donating.

- https://gratipay.com/~architv


text:
- Toggle snippet
- jQuery and vanilla js version

### Dependencies
- [jQuery](http://jquery.com/) (for jQuery version)
- [Velocity.js](http://julian.com/research/velocity/)

### Initialise
```javascript
cargobay.toggle.init();
```

### Usage

This snippet is intended to quickly toggle the state of a component. (For example the opening/closing of a menu.)

The class **js-toggle-btn** is used as a javascript hook to identify the control button. This button also uses the **data-target** attribute to identify the element that it should control. Its value can be any css selector pointing to the **toggle-item**.
Additional data attributes for the **js-toggle-btn** are the following:

 - **data-hide-others**: Boolean to define if a click on this button should close other open items on the same level. Using this attribute also requires you to use the **data-level** attribute. ( See below ).
 - **data-level**: Used with the **data-hide-others** attribute to identify buttons on the same level.
 - **data-duration**: Use this attribute if you want to change the default duration of the animation. Time is in ms and can be unique for each button. ( Default is 150ms )

The **toggle-item** should be a wrapper for the **toggle-item__content** component.

Inside the button you can optionally have a set of icons to illustrate the state of  the item you are hiding/showing. You can toggle these by applying the **toggle-btn__icon--show** (visible by default) and **toggle-btn__icon--hide** (shown when toggled) classes.

```html
<div class="container">
    <h1>Cargo Bay Toggle - jQuery</h1>

	<button class="js-toggle-btn toggle-button toggle-button-demo" data-target="#main-navigation--tabs">
	    Toggle Me!
	    <span class="icon--chevron-down toggle-btn__icon--show"></span>
	    <span class="icon--chevron-up toggle-btn__icon--hide"></span>
	</button>

	<div id="main-navigation--tabs" class="toggle-item toggle-item-demo">
	    <div class="toggle-item__content">
		<p>
		    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quisquam magni inventore nisi enim numquam, accusamus tempore voluptates possimus amet quod aspernatur ea, nulla, sapiente non facere quidem laudantium illo ipsam dolor aliquam dignissimos? Ad non, itaque blanditiis cum impedit, porro dolor nobis. Cupiditate debitis beatae labore, suscipit, dolorem nam omnis?
		</p>
		<p>
		    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quisquam magni inventore nisi enim numquam, accusamus tempore voluptates possimus amet quod aspernatur ea, nulla, sapiente non facere quidem laudantium illo ipsam dolor aliquam dignissimos? Ad non, itaque blanditiis cum impedit, porro dolor nobis. Cupiditate debitis beatae labore, suscipit, dolorem nam omnis?
		</p>
	    </div>
	</div>
</div>
```

### Support
- Latest Chrome
- Latest FireFox
- Latest Safari
- IE9 (IE10 for vanilla version) and up