text:
//
//  CalaosRequest.m
//  CalaosHome
//
//  Created by Raoul on 18/02/11.
//  Copyright 2011 Calaos. All rights reserved.
//

#import "CalaosRequest.h"
#import "JSON.h"
#import "AsyncSocket.h"
#import "AsyncUdpSocket.h"
#import "ASIHTTPRequest.h"

#include <arpa/inet.h>

#ifndef BCAST_UDP_PORT
#define BCAST_UDP_PORT          4545
#endif

#ifndef CALAOS_TCP_PORT
#define CALAOS_TCP_PORT         4456
#endif

enum { TAG_LOGIN, TAG_LISTENDATA };

NSString *const CalaosNotificationConnected = @"CalaosNotificationConnected";
NSString *const CalaosNotificationLoginFailed = @"CalaosNotificationLoginFailed";

NSString *const CalaosNotificationIOChanged = @"CalaosNotificationIOChanged";
NSString *const CalaosNotificationRoomChanged = @"CalaosNotificationRoomChanged";

NSString *const CalaosNotificationReload = @"CalaosNotificationReload";

NSString *const CalaosNotificationAudioVolumeChanged = @"CalaosNotificationAudioVolumeChanged";
NSString *const CalaosNotificationAudioStatusChanged = @"CalaosNotificationAudioStatusChanged";
NSString *const CalaosNotificationAudioPlayerChanged = @"CalaosNotificationAudioPlayerChanged";
NSString *const CalaosNotificationAudioPlaylistChanged = @"CalaosNotificationAudioPlaylistChanged";
NSString *const CalaosNotificationAudioPlaylistItemUpdated = @"CalaosNotificationAudioPlaylistItemUpdated";

NSString *const CalaosNetworkUrl = @"https://www.calaos.fr/calaos_network/api.php";
NSString *const CalaosBoxUrl = @"https://%@/api.php";

static CalaosRequest *sharedInstance = nil;

@interface CalaosRequest (hidden)

- (void)initialization;

- (void)didReceiveMemoryWarning:(NSNotification *)notification;
- (void)willResignActive:(NSNotification *)notification;
- (void)willTerminate:(NSNotification *)notification;

- (void)startUDPSearch;
- (void)timerDetectSendData:(NSTimer *)timer;

@end

@implementation CalaosRequest (hidden)

#pragma mark System notifications

- (void)initialization
{
	NSNotificationCenter *nc  = [NSNotificationCenter defaultCenter];

	[nc addObserver:self
		   selector:@selector(didReceiveMemoryWarning:)
			   name:UIApplicationDidReceiveMemoryWarningNotification
			 object:nil];
	[nc addObserver:self
		   selector:@selector(willResignActive:)
			   name:UIApplicationWillResignActiveNotification
			 object:nil];
	[nc addObserver:self
		   selector:@selector(willTerminate:)
			   name:UIApplicationWillTerminateNotification
			 object:nil];

    requestManager = [[RequestManager alloc] init];

    cacheInputs = [[NSMutableDictionary alloc] init];
    cacheOutputs = [[NSMutableDictionary alloc] init];
    cacheScenarios = [[NSMutableArray alloc] init];
    cacheLightsOn = [[NSMutableArray alloc] init];
    homeArray = nil;

    detectSocket = nil;
    listenSocket = nil;

    uuid = nil;

    cacheTracks = nil;
}

- (void)startUDPSearch
{
    if (detectSocket)
        return;

    NSError *err;

    // Failed to send request to calaos.fr, try to detect the machine with an UDP broadcast
    NSLog(@"Trying to detect on LAN network...");

    NSArray *runLoopCommonModesArray = [NSArray arrayWithObject:NSRunLoopCommonModes];
    detectSocket = [[AsyncUdpSocket alloc] initIPv4];
    [detectSocket setDelegate:self];
    [detectSocket setRunLoopModes:runLoopCommonModesArray];

    if (![detectSocket bindToPort:BCAST_UDP_PORT error:&err])
        NSLog (@"UDP Socket bind failed (%@)!", err);

    [detectSocket receiveWithTimeout:-1 tag:0];

    detectSocketTX = [[AsyncUdpSocket alloc] initIPv4];
    [detectSocketTX setDelegate:self];
    [detectSocketTX setRunLoopModes:runLoopCommonModesArray];

    struct sockaddr_in bcast_addr;
    bcast_addr.sin_family = AF_INET;
    bcast_addr.sin_addr.s_addr = INADDR_NONE;
    bcast_addr.sin_port = htons (BCAST_UDP_PORT);

    if (![detectSocketTX enableBroadcast:TRUE error:&err])
        NSLog (@"Could not enable broadcast on the detectSocket: %@", err);

    if (![detectSocketTX connectToAddress:[NSData dataWithBytes:&bcast_addr length:sizeof(bcast_addr)] error:&err])
        NSLog (@"Could not connect the detectSocket: %@", err);

    detectTimer = [NSTimer scheduledTimerWithTimeInterval:0.5
                                                   target:self
                                                 selector:@selector(timerDetectSendData:)
                                                 userInfo:nil
                                                  repeats:YES];

    detectCount = 0;
}

- (void)timerDetectSendData:(NSTimer *)timer
{
    NSLog(@"Sending UDP search...");

    if (detectCount >= 10)
    {
        NSLog(@"No Calaos machine found on LAN...");

        [detectSocket close];
        [detectSocket autorelease];
        detectSocket = nil;

        [detectSocketTX close];
        [detectSocketTX autorelease];
        detectSocketTX = nil;

        [detectTimer invalidate];
        detectTimer = nil;

        NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];

        [nc postNotificationName:CalaosNotificationLoginFailed
                          object:nil
                        userInfo:nil];

        return;
    }

    NSString *data = @"CALAOS_DISCOVER";
    [detectSocketTX sendData:[data dataUsingEncoding:NSASCIIStringEncoding]
                 withTimeout:-1
                         tag:0];

    detectCount++;
}

- (void)didReceiveMemoryWarning:(NSNotification *)notification
{
}

- (void)willResignActive:(NSNotification *)notification
{
}

- (void)willTerminate:(NSNotification *)notification
{
}

@end

@implementation CalaosRequest

@synthesize username, password, private_ip, public_ip, homeDict;

#pragma mark Getters

- (NSDictionary *)getInputs
{
    return cacheInputs;
}

- (NSDictionary *)getOutputs
{
    return cacheOutputs;
}

- (NSArray *)getScenarios
{
    return cacheScenarios;
}

- (NSArray *)getLightsOn
{
    return cacheLightsOn;
}

- (NSArray *)getHome
{
    NSLog(@"retain count: %d", [homeArray retainCount]);
    return homeArray;
}

- (NSArray *)getCameras
{
    return [self.homeDict objectForKey:@"cameras"];
}

- (NSInteger)getAudioPlayersCount
{
    NSArray *audios = [self.homeDict objectForKey:@"audio"];

    if (!audios)
        return 0;

    return [audios count];
}

- (NSDictionary *)getAudioWithId:(NSInteger)player_id
{
    NSArray *audios = [self.homeDict objectForKey:@"audio"];

    if (!audios)
        return nil;

    if (player_id < 0 || player_id >= [audios count])
        return nil;

    return [audios objectAtIndex:player_id];
}

- (NSString *)getConsigneForInput:(NSString *)input_id
{
    NSEnumerator *iter = [cacheInputs objectEnumerator];
    NSDictionary *input;
    while ((input = [iter nextObject]))
    {
        if (![[input objectForKey:@"type"] isEqualToString:@"InternalInt"] &&
            ![[input objectForKey:@"gui_type"] isEqualToString:@"var_int"])
            continue;

        if ([[input objectForKey:@"chauffage_id"] isEqualToString:[[cacheInputs objectForKey:input_id] objectForKey:@"chauffage_id"]])
            return [input objectForKey:@"id"];
    }

    return nil;
}

#pragma mark Utility functions

- (NSString *)computeCalaosUrl
{
	if (at_home)
		return [NSString stringWithFormat:CalaosBoxUrl, self.private_ip];
	else
		return [NSString stringWithFormat:CalaosBoxUrl, self.public_ip];
}

- (NSString *)computeCameraUrl:(NSString *)camera_url
{
	if (at_home)
		return [NSString stringWithFormat:@"https://%@%@&u=%@&p=%@", self.private_ip, camera_url, [self.username stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding], [self.password stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding]];
	else
		return [NSString stringWithFormat:@"https://%@%@&u=%@&p=%@", self.public_ip, camera_url, [self.username stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding], [self.password stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding]];
}

- (void)updateCache
{
    NSLog(@"UPDATE CACHE");

    [cacheInputs removeAllObjects];
    [cacheOutputs removeAllObjects];
    [cacheScenarios removeAllObjects];
    [cacheLightsOn removeAllObjects];
    if (homeArray)
        [homeArray release];
    homeArray = nil;

    //Update io tables
    homeArray = [NSMutableArray arrayWithArray:[[self.homeDict objectForKey:@"home"] sortedArrayUsingComparator: ^(id obj1, id obj2)
    {
        int hits1, hits2;
        hits1 = [[obj1 objectForKey:@"hits"] integerValue];
        hits2 = [[obj2 objectForKey:@"hits"] integerValue];

        if (hits1 < hits2)
            return (NSComparisonResult)NSOrderedDescending;

        if (hits1 > hits2)
            return (NSComparisonResult)NSOrderedAscending;

        return (NSComparisonResult)NSOrderedSame;
    }]];

    if (!homeArray)
    {
        NSLog(@"Error \"home\" not found in homeArray");

        return;
    }

    NSLog(@"Found %d rooms", [homeArray count]);
    [homeArray retain];

    //remove the Internal room from the list (only for calaos_server < 2.0)
    for (int i = 0;i < [homeArray count];i++)
    {
        if ([[[homeArray objectAtIndex:i] objectForKey:@"type"] isEqualToString:@"Internal"])
        {
            [homeArray removeObjectAtIndex:i];
            break;
        }
    }

    NSEnumerator *enumerator = [homeArray objectEnumerator];
    NSDictionary *room;
    while ((room = [enumerator nextObject]))
    {
        NSDictionary *items = [room objectForKey:@"items"];
        if (!items)
        {
            NSLog(@"Error: items key not found");
            continue;
        }

        NSArray *inputs = [items objectForKey:@"inputs"];

        if ([inputs isKindOfClass:[NSNull class]] || !inputs)
        {
            //create an empty array if it didn't exists
            [items setValue:[NSArray array] forKey:@"inputs"];
            inputs = [items objectForKey:@"inputs"];
        }

        NSEnumerator *iter = [inputs objectEnumerator];
        NSDictionary *input;
        while ((input = [iter nextObject]))
        {
            [cacheInputs setObject:input forKey:[input objectForKey:@"id"]];

            //item is not visible. Show all scenarios event they are not visible
            //if (![[input objectForKey:@"visible"] isEqualToString:@"true"])
            //    continue;

            //item is scenario
            if ([[input objectForKey:@"type"] isEqualToString:@"scenario"] ||
                [[input objectForKey:@"type"] isEqualToString:@"Scenario"] ||
                [[input objectForKey:@"gui_type"] isEqualToString:@"scenario"])
            {
                [cacheScenarios addObject:input];
            }
        }

        NSArray *outputs = [items objectForKey:@"outputs"];

        if ([outputs isKindOfClass:[NSNull class]] || !outputs)
        {
            //create an empty array if it didn't exists
            [items setValue:[NSArray array] forKey:@"outputs"];
            outputs = [items objectForKey:@"outputs"];
        }

        iter = [outputs objectEnumerator];
        NSDictionary *output;
        while ((output = [iter nextObject]))
        {
            [cacheOutputs setObject:output forKey:[output objectForKey:@"id"]];

            //item is not visible
            if (![[output objectForKey:@"visible"] isEqualToString:@"true"])
                continue;

            //item is WODigital, WODali or WODaliRGB
            if (([[output objectForKey:@"type"] isEqualToString:@"WODigital"] &&
                 [[output objectForKey:@"gtype"] isEqualToString:@"light"]) ||
                [[output objectForKey:@"type"] isEqualToString:@"WODali"] ||
                [[output objectForKey:@"type"] isEqualToString:@"WODaliRVB"] ||
                [[output objectForKey:@"gui_type"] isEqualToString:@"light"])
            {
                NSString *state = [output objectForKey:@"state"];
                if ([state isEqualToString:@"true"])
                {
                    [cacheLightsOn addObject:output];
                }
                else
                {
                    double value = [state doubleValue];
                    if (value > 0)
                        [cacheLightsOn addObject:output];
                }
            }
        }
    }
}

#pragma mark Singleton

+ (CalaosRequest *)sharedInstance
{
    if (!sharedInstance)
	{
        sharedInstance = [[super allocWithZone:NULL] init];
		[sharedInstance initialization];
    }

    return sharedInstance;
}

+ (id)allocWithZone:(NSZone*)zone
{
    return [[self sharedInstance] retain];
}

- (id)copyWithZone:(NSZone *)zone
{
    return self;
}

- (id)retain
{
    return self;
}

- (NSUInteger)retainCount
{
    return NSUIntegerMax;
}

- (void)release
{
    /*
     SHOULD NOT DO THIS HERE!
     this is a singleton instance

    [cacheScenarios release];
    [cacheLightsOn release];
    [cacheInputs release];
    [cacheOutputs release];

	[username release];
	[password release];
	[public_ip release];
	[private_ip release];
	[homeDict release];
	[queueRequests cancelAllOperations];
	[queueRequests release];*/
}

- (id)autorelease
{
    return self;
}

#pragma mark Calaos Requests

- (void)loadHome
{
    //Now that we know the ip address of the remote calaos system,
    //we can load the entire home.
    NSURL *url = [NSURL URLWithString:[self computeCalaosUrl]];

    NSString *data = [NSString stringWithFormat:@"{\"cn_user\":\"%@\",\"cn_pass\":\"%@\",\"action\":\"get_home\"}", self.username, self.password];
    NSLog(@"Sending request: %@ to : %@", data, [self computeCalaosUrl]);

    [requestManager sendRequest:url
                       withData:data
                    andDelegate:self
                    andSelector:@selector(requestGetHomeFinished:withUserData:)];
}

- (void)reloadHome
{
    //reload the entire home.
    if (at_home)
    {
        if (listenSocket)
        {
            [listenSocket setDelegate:nil];
            [listenSocket disconnect];
            [listenSocket release];
            listenSocket = nil;
        }

        if (detectSocket)
        {
            [detectSocket setDelegate:nil];
            [detectSocket close];
            [detectSocket release];
            detectSocket = nil;
        }

        if (detectSocketTX)
        {
            [detectSocketTX setDelegate:nil];
            [detectSocketTX close];
            [detectSocketTX release];
            detectSocketTX = nil;
        }
    }

    if (cacheTracks)
    {
        [cacheTracks release];
        cacheTracks = nil;
    }

    [self loadHome];
}

- (void)reconnectAll
{
    NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
    [nc postNotificationName:CalaosNotificationLoginFailed
                      object:nil
                    userInfo:nil];

    if (listenSocket)
    {
        [listenSocket setDelegate:nil];
        [listenSocket disconnect];
        [listenSocket release];
        listenSocket = nil;
    }

    if (detectSocket)
    {
        [detectSocket setDelegate:nil];
        [detectSocket close];
        [detectSocket release];
        detectSocket = nil;
    }

    if (detectSocketTX)
    {
        [detectSocketTX setDelegate:nil];
        [detectSocketTX close];
        [detectSocketTX release];
        detectSocketTX = nil;
    }

    if (cacheTracks)
    {
        [cacheTracks release];
        cacheTracks = nil;
    }

    [self connectWithUsername:self.username andPassword:self.password];
}

- (void)requestUpdatedAudioPlayerFinished:(NSString *)responseString withUserData:(id)userData
{
	NSDictionary *dict = [responseString JSONValue];

	if (!dict)
        return;

    NSArray *audio_players = [dict objectForKey:@"audio_players"];

    if ([audio_players isKindOfClass:[NSNull class]])
        return;

    NSEnumerator *iter = [audio_players objectEnumerator];
    NSDictionary *player;
    while ((player = [iter nextObject]))
    {
        int num = [[player objectForKey:@"player_id"] doubleValue];

        NSDictionary *aplayer = [self getAudioWithId:num];
        if (!aplayer)
            continue;

        //Update player infos
        [aplayer setValuesForKeysWithDictionary:player];

        NSDictionary *userData = [NSDictionary dictionaryWithObjectsAndKeys:
                                  [player objectForKey:@"player_id"], @"player_id",
                                  nil];

        NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
        [nc postNotificationName:CalaosNotificationAudioPlayerChanged
                          object:nil
                        userInfo:userData];
    }
}

- (void)updateAudioPlayer:(NSInteger)num
{
    NSURL *url = [NSURL URLWithString:[self computeCalaosUrl]];
    NSString *data = [NSString stringWithFormat:@"{\"cn_user\":\"%@\",\"cn_pass\":\"%@\",\"action\":\"get_state\",\"audio_players\": [\"%d\"]}", self.username, self.password, num];

    [requestManager sendRequest:url
                       withData:data
                    andDelegate:self
                    andSelector:@selector(requestUpdatedAudioPlayerFinished:withUserData:)];
}

- (void)processListenNotification:(NSString *)calaos_event
{
    NSLog(@"Got Calaos Event: %@", calaos_event);

    NSMutableArray *tokens = [NSMutableArray arrayWithArray:[calaos_event componentsSeparatedByString: @" "]];

    if ([tokens count] < 2)
    {
        //drop, this is probably not a calaos event...
        return;
    }

    //Url decode parameters
    for (int i = 0;i < [tokens count];i++)
    {
        [tokens replaceObjectAtIndex:i withObject:[[tokens objectAtIndex:i] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
    }

    if ([[tokens objectAtIndex:0] isEqualToString:@"input"] ||
        [[tokens objectAtIndex:0] isEqualToString:@"output"])
    {
        NSMutableDictionary *dict;

        if ([[tokens objectAtIndex:0] isEqualToString:@"input"])
            dict = cacheInputs;
        else
            dict = cacheOutputs;

        NSArray *tokensChange = [[tokens objectAtIndex:2] componentsSeparatedByString: @":"];
        if ([tokensChange count] == 2)
        {
            NSString *value = [tokensChange objectAtIndex:1];
            NSString *key = [tokensChange objectAtIndex:0];

            [[dict objectForKey:[tokens objectAtIndex:1]] setValue:value forKey:key];
        }

        NSDictionary *userData = [NSDictionary dictionaryWithObjectsAndKeys:
                                  [tokens objectAtIndex:0], @"type",
                                  [tokens objectAtIndex:1], @"id",
                                  [tokens objectAtIndex:2], @"change",
                                  nil];

        //Notify IO change
        NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
        [nc postNotificationName:CalaosNotificationIOChanged
                          object:nil
                        userInfo:userData];
    }
    else if ([[tokens objectAtIndex:0] isEqualToString:@"new_output"] ||
             [[tokens objectAtIndex:0] isEqualToString:@"new_input"])
    {
        //We don't notify here, but just reload home
        [self reloadHome];
    }
    else if ([[tokens objectAtIndex:0] isEqualToString:@"delete_output"] ||
             [[tokens objectAtIndex:0] isEqualToString:@"delete_input"])
    {
        //We don't notify here, but just reload home
        [self reloadHome];
    }
    else if ([[tokens objectAtIndex:0] isEqualToString:@"modify_room"])
    {
        //Notify modify room
        NSDictionary *userData;

        if ([[[tokens objectAtIndex:1] substringToIndex:14] isEqualToString:@"old_room_name:"])
        {
            userData = [NSDictionary dictionaryWithObjectsAndKeys:
                        [[tokens objectAtIndex:1] substringFromIndex:14], @"old_room_name",
                        [[tokens objectAtIndex:2] substringFromIndex:14], @"new_room_name",
                        [[tokens objectAtIndex:3] substringFromIndex:10], @"room_type",
                        nil];
        }
        else if ([[[tokens objectAtIndex:1] substringToIndex:14] isEqualToString:@"old_room_type:"])
        {
            userData = [NSDictionary dictionaryWithObjectsAndKeys:
                        [[tokens objectAtIndex:1] substringFromIndex:14], @"old_room_type",
                        [[tokens objectAtIndex:2] substringFromIndex:14], @"new_room_type",
                        [[tokens objectAtIndex:3] substringFromIndex:10], @"room_name",
                        nil];
        }
        else if ([[[tokens objectAtIndex:1] substringToIndex:14] isEqualToString:@"old_room_hits:"])
        {
            userData = [NSDictionary dictionaryWithObjectsAndKeys:
                        [[tokens objectAtIndex:1] substringFromIndex:14], @"old_room_hits",
                        [[tokens objectAtIndex:2] substringFromIndex:14], @"new_room_hits",
                        [[tokens objectAtIndex:3] substringFromIndex:10], @"room_name",
                        [[tokens objectAtIndex:3] substringFromIndex:10], @"room_type",
                        nil];
        }

        //Notify room change
        NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
        [nc postNotificationName:CalaosNotificationRoomChanged
                          object:nil
                        userInfo:userData];
    }
    else if ([[tokens objectAtIndex:0] isEqualToString:@"delete_room"])
    {
        //We don't notify here, but just reload home
        [self reloadHome];
    }
    else if ([[tokens objectAtIndex:0] isEqualToString:@"new_room"])
    {
        //We don't notify here, but just reload home
        [self reloadHome];
    }
    else if ([[tokens objectAtIndex:0] isEqualToString:@"audio_volume"])
    {
        NSDictionary *player = [self getAudioWithId:[[tokens objectAtIndex:1] doubleValue]];

        if (!player)
            return;

        if ([tokens count] < 4)
            return;

        NSDictionary *userData = [NSDictionary dictionaryWithObjectsAndKeys:
                                  [tokens objectAtIndex:1], @"player_id",
                                  [tokens objectAtIndex:3], @"volume",
                                  nil];

        [player setValue:[tokens objectAtIndex:3] forKey:@"volume"];

        NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
        [nc postNotificationName:CalaosNotificationAudioVolumeChanged
                          object:nil
                        userInfo:userData];
    }
    else if ([[tokens objectAtIndex:0] isEqualToString:@"audio_status"])
    {
        NSDictionary *userData = [NSDictionary dictionaryWithObjectsAndKeys:
                                  [tokens objectAtIndex:1], @"player_id",
                                  [tokens objectAtIndex:2], @"status",
                                  nil];

        NSDictionary *player = [self getAudioWithId:[[tokens objectAtIndex:1] doubleValue]];

        if (!player)
            return;

        [player setValue:[tokens objectAtIndex:2] forKey:@"status"];

        NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
        [nc postNotificationName:CalaosNotificationAudioStatusChanged
                          object:nil
                        userInfo:userData];
    }
    else if ([[tokens objectAtIndex:0] isEqualToString:@"audio"] &&
             [[tokens objectAtIndex:2] isEqualToString:@"songchanged"])
    {
        [self updateAudioPlayer:[[tokens objectAtIndex:1] doubleValue]];
    }

}

- (void)requestGetPollingFinished:(NSString *)responseString withUserData:(id)userData
{
	NSDictionary *dict = [responseString JSONValue];

	if (!dict)
        return;

    NSArray *events = [dict objectForKey:@"events"];

    NSEnumerator *iter = [events objectEnumerator];
    NSString *msg;
    while ((msg = [iter nextObject]))
    {
        if ([msg length] > 2)
            [self processListenNotification:msg];
    }

    //Start the "listen polling"
    [self performSelector:@selector(startListenPolling:) withObject:nil afterDelay:1.0];
}

- (void)startListenPolling:(id)unused
{
    NSURL *url = [NSURL URLWithString:[self computeCalaosUrl]];
    NSString *data = [NSString stringWithFormat:@"{\"cn_user\":\"%@\",\"cn_pass\":\"%@\",\"action\":\"poll_listen\",\"type\":\"get\",\"uuid\":\"%@\"}", self.username, self.password, uuid];

    [requestManager sendRequest:url
                       withData:data
                    andDelegate:self
                    andSelector:@selector(requestGetPollingFinished:withUserData:)];
}

- (void)requestGetUUIDFinished:(NSString *)responseString withUserData:(id)userData
{
	NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
	NSDictionary *dict = [responseString JSONValue];

	if (!dict)
	{
		[nc postNotificationName:CalaosNotificationLoginFailed
						  object:nil
						userInfo:nil];

        return;
	}

    uuid = [[NSString stringWithString:[dict objectForKey:@"uuid"]] retain];

    //Start the "listen polling"
    [self performSelector:@selector(startListenPolling:) withObject:nil afterDelay:1.0];
}

- (void)requestGetHomeFinished:(NSString *)responseString withUserData:(id)userData
{
    [ASIHTTPRequest hideNetworkActivityIndicator];

	NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];

	self.homeDict = [responseString JSONValue];

    [self updateCache];

	if (!self.homeDict)
	{
		[nc postNotificationName:CalaosNotificationLoginFailed
						  object:nil
						userInfo:nil];

        return;
	}

	NSArray *_homeArray = [self.homeDict objectForKey:@"home"];

	if (_homeArray)
	{
		[nc postNotificationName:CalaosNotificationConnected
						  object:nil
						userInfo:nil];

        [nc postNotificationName:CalaosNotificationReload
						  object:nil
						userInfo:nil];

        if (at_home)
        {
            //connect the listening Socket for events
            NSArray *runLoopCommonModesArray = [NSArray arrayWithObject:NSRunLoopCommonModes];
            listenSocket = [[AsyncSocket alloc] initWithDelegate:self];
            [listenSocket setRunLoopModes:runLoopCommonModesArray];

            NSError *err;
            if (![listenSocket connectToHost:self.private_ip onPort:CALAOS_TCP_PORT withTimeout:2.0 error:&err])
                NSLog(@"TCP Listen socket, can't connect: %@", err);
        }
        else
        {
            //Get a uuid for poll_listen
            NSURL *url = [NSURL URLWithString:[self computeCalaosUrl]];
            NSString *data = [NSString stringWithFormat:@"{\"cn_user\":\"%@\",\"cn_pass\":\"%@\",\"action\":\"poll_listen\",\"type\":\"register\"}", self.username, self.password];

            [requestManager sendRequest:url
                               withData:data
                            andDelegate:self
                            andSelector:@selector(requestGetUUIDFinished:withUserData:)];
        }
	}
	else
	{
		[nc postNotificationName:CalaosNotificationLoginFailed
						  object:nil
						userInfo:nil];
	}
}

- (void)requestGetIPFinished:(NSString *)responseString withUserData:(id)userData
{
	NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];

    if (!responseString)
    {
        if ([ASIHTTPRequest isNetworkReachableViaWWAN])
        {
            [ASIHTTPRequest hideNetworkActivityIndicator];

            NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];

            [nc postNotificationName:CalaosNotificationLoginFailed
                              object:nil
                            userInfo:nil];
        }
        else
        {
            [self startUDPSearch];
        }
    }

	NSDictionary *dict = [responseString JSONValue];

	if (!dict)
	{
		[nc postNotificationName:CalaosNotificationLoginFailed
						  object:nil
						userInfo:nil];
	}

	if ([[dict objectForKey:@"cn_user"] isEqualToString:self.username])
	{
		self.private_ip = [dict objectForKey:@"private_ip"];
		self.public_ip = [dict objectForKey:@"public_ip"];

		id boolNumber = [dict objectForKey:@"at_home"];
		if ((boolNumber) && [boolNumber isKindOfClass:[NSNumber class]])
			at_home = [boolNumber boolValue];

		[self loadHome];
	}
	else
	{
        [ASIHTTPRequest hideNetworkActivityIndicator];

		[nc postNotificationName:CalaosNotificationLoginFailed
						  object:nil
						userInfo:nil];
	}

}

/*
- (void)requestFailed:(ASIHTTPRequest *)_request
{
    [ASIHTTPRequest hideNetworkActivityIndicator];

	NSError *error = [_request error];
	NSLog(@"Failed request: %@", [error localizedDescription]);

	[self reconnectAll];
}

- (void)requestIPFailed:(ASIHTTPRequest *)_request
{
	NSError *error = [_request error];
	NSLog(@"Failed request: %@", [error localizedDescription]);

    if ([ASIHTTPRequest isNetworkReachableViaWWAN])
    {
        [ASIHTTPRequest hideNetworkActivityIndicator];

        NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];

        [nc postNotificationName:CalaosNotificationLoginFailed
                          object:nil
                        userInfo:nil];
    }
    else
    {
        [self startUDPSearch];
    }
}
*/

- (void)connectWithUsername:(NSString *)_username andPassword:(NSString *)_password
{
    [ASIHTTPRequest setShouldUpdateNetworkActivityIndicator:NO];
    [ASIHTTPRequest showNetworkActivityIndicator];

	self.username = _username;
	self.password = _password;

	NSURL *url = [NSURL URLWithString:CalaosNetworkUrl];
	NSString *data = [NSString stringWithFormat:@"{\"cn_user\":\"%@\",\"cn_pass\":\"%@\",\"action\":\"get_ip\"}", self.username, self.password];

	[requestManager sendRequest:url
                       withData:data
             verifyCertificates:YES
                    andDelegate:self
                    andSelector:@selector(requestGetIPFinished:withUserData:)];
}

- (void)requestCameraFinished:(NSData *)responseData withUserData:(id)userData
{
    id obj = [userData objectForKey:@"delegate"];
    SEL doneSelector = NSSelectorFromString([userData objectForKey:@"selector"]);

    if (obj && [obj respondsToSelector:doneSelector])
        [obj performSelector:doneSelector withObject:responseData];
}

- (void)getPictureForCamera:(NSInteger)num withDelegate:(id)obj andDoneSelector:(SEL)doneSelector
{
    if (num >= [[self getCameras] count] || num < 0)
    {
        if (obj && [obj respondsToSelector:doneSelector])
            [obj performSelector:doneSelector withObject:nil];

        return;
    }

    NSString *camUrl = [[[self getCameras] objectAtIndex:num] objectForKey:@"url_lowres"];

    if (!camUrl)
    {
        if (obj && [obj respondsToSelector:doneSelector])
            [obj performSelector:doneSelector withObject:NO withObject:nil];

        return;
    }

    NSURL *url = [NSURL URLWithString:[self computeCameraUrl:camUrl]];

    NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
                              obj, @"delegate",
                              NSStringFromSelector(doneSelector), @"selector",
                              nil];

    [requestManager sendRequest:url
                       withData:nil
             verifyCertificates:NO
                    wantsString:NO
                    andDelegate:self
                    andSelector:@selector(requestCameraFinished:withUserData:)
                    andUserData:userInfo];
}

- (void)getCoverForAudio:(NSInteger)num withDelegate:(id)obj andDoneSelector:(SEL)doneSelector
{
    NSDictionary *audio = [self getAudioWithId:num];

    if (!audio)
    {
        if (obj && [obj respondsToSelector:doneSelector])
            [obj performSelector:doneSelector withObject:nil];

        return;
    }

    NSString *coverUrl = [audio objectForKey:@"cover_url"];

    if (!coverUrl)
    {
        if (obj && [obj respondsToSelector:doneSelector])
            [obj performSelector:doneSelector withObject:NO withObject:nil];

        return;
    }

    NSURL *url = [NSURL URLWithString:[self computeCameraUrl:coverUrl]];
    NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:obj, @"delegate", NSStringFromSelector(doneSelector), @"selector", nil];

    [requestManager sendRequest:url
                       withData:nil
             verifyCertificates:NO
                    wantsString:NO
                    andDelegate:self
                    andSelector:@selector(requestCameraFinished:withUserData:)
                    andUserData:userInfo];
}

- (void)requestSendFinished:(NSString *)responseString withUserData:(id)userData
{
    if (responseString)
        NSLog(@"Command sent, result: %@", responseString);
    else
        NSLog(@"Command failed !");
}

- (void)sendAction:(NSString *)type withId:(NSString *)calaos_id andValue:(NSString *)value
{
    NSURL *url = [NSURL URLWithString:[self computeCalaosUrl]];

    NSString *_type;
    if ([type isEqualToString:@"audio"])
        _type = [NSString stringWithFormat:@"\"player_id\":\"%@\"", calaos_id];
    else
        _type = [NSString stringWithFormat:@"\"id\":\"%@\"", calaos_id];

    NSString *data = [NSString stringWithFormat:@"{\"cn_user\":\"%@\",\"cn_pass\":\"%@\",\"action\":\"set_state\",\"type\":\"%@\",%@,\"value\":\"%@\"}", username, password, type, _type, value];

    [requestManager sendRequest:url
                       withData:data
             verifyCertificates:NO
                    andDelegate:self
                    andSelector:@selector(request)];
}

- (void)sendCameraAction:(NSString *)action forCamera:(NSString *)camera_id withValue:(NSString *)value
{
    NSURL *url = [NSURL URLWithString:[self computeCalaosUrl]];

    NSString *data = [NSString stringWithFormat:@"{\"cn_user\":\"%@\",\"cn_pass\":\"%@\",\"action\":\"set_state\",\"type\":\"camera\",\"camera_id\":\"%@\",\"camera_action\":\"%@\",\"value\":\"%@\"}", username, password, camera_id, action, value];

    [requestManager sendRequest:url
                       withData:data
             verifyCertificates:NO
                    andDelegate:self
                    andSelector:@selector(request)];
}

- (void)sendAudioFile:(NSString *)soundFilePath toPlayer:(NSInteger)playerId withDelegate:(id)obj andDoneSelector:(SEL)doneSelector
{
    NSLog(@"Sending file: %@", soundFilePath);
    /*
    NSString *data = [NSString stringWithFormat:@"{\"cn_user\":\"%@\",\"cn_pass\":\"%@\",\"action\":\"play_file\",\"player_id\":\"%d\"}", username, password, playerId];

    NSURL *url = [NSURL URLWithString:[self computeCalaosUrl]];
    ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
    [request setDelegate:self];
    [request setDidFinishSelector:@selector(requestSendFinishedCallback:)];
    [request setDidFailSelector:@selector(requestSendFailedCallback:)];
    [request setShouldAttemptPersistentConnection:NO];
    request.validatesSecureCertificate = FALSE;

    NSLog(@"Sending request: %@", data);

    [request setPostValue:data forKey:@"json"];
    [request setFile:soundFilePath forKey:@"file"];

    NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:obj, @"delegate", NSStringFromSelector(doneSelector), @"selector", nil];
    request.userInfo = userInfo;

    [queueRequests addOperation:request];*/
}

- (void)requestSendFinishedCallback:(ASIHTTPRequest *)_request
{
    id obj = [_request.userInfo objectForKey:@"delegate"];
    SEL doneSelector = NSSelectorFromString([_request.userInfo objectForKey:@"selector"]);

    if (obj && [obj respondsToSelector:doneSelector])
        [obj performSelector:doneSelector withObject:[_request responseData]];
}

- (void)requestSendFailedCallback:(ASIHTTPRequest *)_request
{
    id obj = [_request.userInfo objectForKey:@"delegate"];
    SEL doneSelector = NSSelectorFromString([_request.userInfo objectForKey:@"selector"]);

    if (obj && [obj respondsToSelector:doneSelector])
        [obj performSelector:doneSelector withObject:nil];
}

- (NSString *)getPlaylistTrackTitle:(NSInteger)track_id forPlayer:(NSInteger)player_id;
{
    NSLog(@"Getting track %d for player %d", track_id, player_id);

    if (cacheTracks && [cacheTracks count] != [self getAudioPlayersCount])
    {
        [cacheTracks release];
        cacheTracks = nil;
    }

    if (!cacheTracks)
    {
        cacheTracks = [[NSMutableArray alloc] init];

        for (int i = 0;i < [self getAudioPlayersCount];i++)
        {
            [cacheTracks addObject:[NSMutableArray array]];
        }
    }

    NSMutableArray *playlist = [cacheTracks objectAtIndex:player_id];

    if (track_id >= [playlist count])
    {
        NSInteger from, num_count = 20;

        if (isLoadingTracks > 0)
        {
            if (track_id > loadTracksCacheCount)
                from = loadTracksCacheCount;
            else
                return nil;
        }
        else
        {
            from = [playlist count];
        }

        if (track_id > from + num_count)
            num_count = track_id - from + 20;

        //Need to load more tracks
        NSURL *url = [NSURL URLWithString:[self computeCalaosUrl]];

        isLoadingTracks++;

        NSString *data = [NSString stringWithFormat:@"{\"cn_user\":\"%@\",\"cn_pass\":\"%@\",\"action\":\"get_playlist\",\"player_id\":\"%d\",\"from\":\"%d\",\"to\":\"%d\"}", username, password, player_id, from, from + num_count];

        NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
                                         [NSNumber numberWithInteger:player_id], @"player_id",
                                    nil];

        [requestManager sendRequest:url
                           withData:data
                 verifyCertificates:NO
                        andDelegate:self
                        andSelector:@selector(requestGetPlaylistFinished:withUserData:)
                        andUserData:userInfo];

        loadTracksCacheCount = from + num_count;

        return nil;
    }

    return [playlist objectAtIndex:track_id];
}

- (void)requestGetPlaylistFinished:(NSString *)responseString withUserData:(id)userData
{
	NSDictionary *dict = [responseString JSONValue];

    NSString *title = @"";

    NSInteger player_id = [[userData objectForKey:@"player_id"] integerValue];
    NSMutableArray *playlist = [cacheTracks objectAtIndex:player_id];

    isLoadingTracks--;

    NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];

    if (playlist)
    {
        NSArray *items = [dict objectForKey:@"items"];

        NSEnumerator *enumerator = [items objectEnumerator];
        NSDictionary *item;
        while ((item = [enumerator nextObject]))
        {
            NSInteger it = [[item objectForKey:@"item"] doubleValue];

            if ([item objectForKey:@"artist"] && [item objectForKey:@"title"])
                title = [NSString stringWithFormat:@"%@ - %@", [item objectForKey:@"artist"], [item objectForKey:@"title"]];

            if ([item objectForKey:@"artist"])
                title = [NSString stringWithFormat:@"%@", [item objectForKey:@"artist"]];

            if ([item objectForKey:@"title"])
                title = [NSString stringWithFormat:@"%@", [item objectForKey:@"title"]];

            //update cache
            if (it >= [playlist count])
            {

                while (it > [playlist count])
                {
                    [playlist addObject:[NSNull null]];
                }

                [playlist addObject:title];
            }
            else
            {
                [playlist replaceObjectAtIndex:it withObject:title];
            }

            NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
                                      [NSNumber numberWithInteger:player_id], @"player_id",
                                      [NSNumber numberWithInteger:it], @"track_id",
                                      title, @"title",
                                      nil];

            [nc postNotificationName:CalaosNotificationAudioPlaylistItemUpdated
                              object:nil
                            userInfo:userInfo];
        }
    }
}

#pragma mark UDP Socket delegate

- (void)onUdpSocket:(AsyncUdpSocket *)sock didNotSendDataWithTag:(long)tag dueToError:(NSError *)error
{
    NSLog(@"Error, UDP packet not sent : %@", [error localizedDescription]);
}

- (BOOL)onUdpSocket:(AsyncUdpSocket *)sock didReceiveData:(NSData *)data withTag:(long)tag fromHost:(NSString *)host port:(UInt16)port
{
    [detectSocket receiveWithTimeout:-1 tag:0];

    NSString *sdata = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];

    if ([sdata length] < 10)
    {
        [sdata release];

        return FALSE;
    }

    if ([[sdata substringToIndex:10] isEqualToString:@"CALAOS_IP "])
    {
        NSMutableString *str = [NSMutableString stringWithString:[sdata substringFromIndex:10]];
        at_home = YES;

        //Clean ending \0 chars
        while ([str characterAtIndex:[str length] - 1] == '\0')
            [str deleteCharactersInRange:NSMakeRange([str length] - 1, 1)];

        self.private_ip = str;
        str = nil;

        NSLog(@"Found Calaos IP on LAN: %@", self.private_ip);

        [detectSocket setDelegate:nil];
        [detectSocket close];
        [detectSocket autorelease];
        detectSocket = nil;

        [detectSocketTX setDelegate:nil];
        [detectSocketTX close];
        [detectSocketTX autorelease];
        detectSocketTX = nil;

        [detectTimer invalidate];
        detectTimer = nil;

        [self loadHome];
    }

    [sdata release];

    return TRUE;
}

#pragma mark Listen socket delegate

- (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
{
    NSLog(@"Connection to %@ done.", host);

    NSLog(@"Login TCP protocol");

    NSString *data = [NSString stringWithFormat:@"login %@ %@\n", username, password];
    [listenSocket writeData:[data dataUsingEncoding:NSASCIIStringEncoding]
                withTimeout:-1
                        tag:TAG_LOGIN];

    [listenSocket readDataWithTimeout:2.0 tag:TAG_LOGIN];
}

- (void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err
{
    NSLog(@"Disconnection happened: %@", err);
}

- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
    NSMutableString *sdata = [[NSMutableString alloc] initWithData:data encoding:NSASCIIStringEncoding];

    NSLog(@"Data read: %@", sdata);

    switch (tag)
    {
        case TAG_LOGIN:
        {
            //Clean ending chars
            while ([sdata characterAtIndex:[sdata length] - 1] == '\n' ||
                   [sdata characterAtIndex:[sdata length] - 1] == '\r')
                [sdata deleteCharactersInRange:NSMakeRange([sdata length] - 1, 1)];

            if ([[sdata stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]
                 isEqualToString:[NSString stringWithFormat:@"login %@ ok", username]])
            {
                NSLog(@"Login OK.");

                NSString *data = @"listen\n";
                [listenSocket writeData:[data dataUsingEncoding:NSASCIIStringEncoding]
                            withTimeout:-1
                                    tag:TAG_LISTENDATA];

                [listenSocket readDataWithTimeout:-1 tag:TAG_LISTENDATA];
            }
            else
            {
                NSLog(@"Login Failed!");

                NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];

                [nc postNotificationName:CalaosNotificationLoginFailed
                                  object:nil
                                userInfo:nil];
            }
            break;
        }
        case TAG_LISTENDATA:
        {
            [listenSocket readDataWithTimeout:-1 tag:TAG_LISTENDATA];

            NSRange r = [sdata rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@"\n\r"]];
            if (r.location == NSNotFound)
            {
                //We have not a complete packet yet, buffurize it.
                [listenBuffer stringByAppendingString:sdata];

                NSLog(@"Listen socket: Bufferize data");

                break;
            }

            if ([listenBuffer length] > 0)
            {
                sdata = listenBuffer;
                listenBuffer = [NSMutableString string];
            }

            //Clean ending chars
            while ([sdata characterAtIndex:[sdata length] - 1] == '\n' ||
                   [sdata characterAtIndex:[sdata length] - 1] == '\r' ||
                   [sdata characterAtIndex:[sdata length] - 1] == '\0')
                [sdata deleteCharactersInRange:NSMakeRange([sdata length] - 1, 1)];

            [sdata replaceOccurrencesOfString:@"\n\r" withString:@"\n" options:NSLiteralSearch range:NSMakeRange(0, [sdata length])];
            [sdata replaceOccurrencesOfString:@"\r" withString:@"\n" options:NSLiteralSearch range:NSMakeRange(0, [sdata length])];

            NSArray *tokens = [sdata componentsSeparatedByString: @"\n"];

            NSEnumerator *iter = [tokens objectEnumerator];
            NSString *msg;
            while ((msg = [iter nextObject]))
            {
                [self processListenNotification:msg];
            }

            break;
        }
        default:
            break;
    }

    [sdata release];
}

- (void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag
{
    NSLog(@"Data written");
}

- (void)onSocketDidDisconnect:(AsyncSocket *)sock
{
    NSLog(@"Socket disconnected");

  	[self reconnectAll];
}

@end


text:
package ninja.siden.okite.compiler.internal;

import java.io.PrintWriter;

import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;

import ninja.siden.okite.annotation.Validate;
import ninja.siden.okite.compiler.Env;
import ninja.siden.okite.compiler.RoundEnvironment;
import ninja.siden.okite.compiler.emitter.ValidateEmitter;

/**
 * @author taichi
 */
public class MethodConstraintGroup extends ConstraintGroup {

	public MethodConstraintGroup(Env env, Element member) {
		super(env, member);
	}

	public static MethodConstraintGroup of(Env env, ExecutableElement ee) {
		return new MethodConstraintGroup(env, ee);
	}

	@Override
	protected void emitBody(RoundEnvironment roundEnv, PrintWriter pw) {
		ValidateEmitter emitter = new ValidateEmitter();
		this.env.elemUtils
				.findAnnotation(this.member, Validate.class)
				.findFirst()
				.map(am -> emitter.emit(env, roundEnv, pw, am, this.member,
						null));
	}
}

redpajama_set_name: RedPajamaGithub


text:
#import "AWEncoderManager.h"
#import "AWSWFaacEncoder.h"
#import "AWSWX264Encoder.h"
#import "AWHWAACEncoder.h"
#import "AWHWH264Encoder.h"

@interface AWEncoderManager()
//编码器
@property (nonatomic, strong) AWVideoEncoder *videoEncoder;
@property (nonatomic, strong) AWAudioEncoder *audioEncoder;
@end

@implementation AWEncoderManager

-(void) openWithAudioConfig:(AWAudioConfig *) audioConfig videoConfig:(AWVideoConfig *) videoConfig{
    switch (self.audioEncoderType) {
        case AWAudioEncoderTypeHWAACLC:
            self.audioEncoder = [[AWHWAACEncoder alloc] init];
            break;
        case AWAudioEncoderTypeSWFAAC:
            self.audioEncoder = [[AWSWFaacEncoder alloc] init];
            break;
        default:
            NSLog(@"[E] AWEncoderManager.open please assin for audioEncoderType");
            return;
    }
    switch (self.videoEncoderType) {
        case AWVideoEncoderTypeHWH264:
            self.videoEncoder = [[AWHWH264Encoder alloc] init];
            break;
        case AWVideoEncoderTypeSWX264:
            self.videoEncoder = [[AWSWX264Encoder alloc] init];
            break;
        default:
            NSLog(@"[E] AWEncoderManager.open please assin for videoEncoderType");
            return;
    }

    self.audioEncoder.audioConfig = audioConfig;
    self.videoEncoder.videoConfig = videoConfig;

    self.audioEncoder.manager = self;
    self.videoEncoder.manager = self;

    [self.audioEncoder open];
    [self.videoEncoder open];
}

-(void)close{
    [self.audioEncoder close];
    [self.videoEncoder close];

    self.audioEncoder = nil;
    self.videoEncoder = nil;

    self.timestamp = 0;

    self.audioEncoder = AWAudioEncoderTypeNone;
    self.videoEncoder = AWVideoEncoderTypeNone;
}

@end


text:
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Numerics;
using System.Text;

namespace SharpFont {
    // raw SFNT container table reading routines
    unsafe static class SfntTables {
        public static uint[] ReadTTCHeader (DataReader reader) {
            // read the file header; if we have a collection, we want to
            // figure out where all the different faces are in the file
            // if we don't have a collection, there's just one font in the file
            var tag = reader.ReadUInt32();
            if (tag != FourCC.Ttcf)
                return new[] { 0u };

            // font file is a TrueType collection; read the TTC header
            reader.Skip(4);     // version number
            var count = reader.ReadUInt32BE();
            if (count == 0 || count > MaxFontsInCollection)
                throw new InvalidFontException("Invalid TTC header");

            var offsets = new uint[count];
            for (int i = 0; i < count; i++)
                offsets[i] = reader.ReadUInt32BE();

            return offsets;
        }

        public static TableRecord[] ReadFaceHeader (DataReader reader) {
            var tag = reader.ReadUInt32BE();
            if (tag != TTFv1 && tag != TTFv2 && tag != FourCC.True)
                throw new InvalidFontException("Unknown or unsupported sfnt version.");

            var tableCount = reader.ReadUInt16BE();
            reader.Skip(6); // skip the rest of the header

            // read each font table descriptor
            var tables = new TableRecord[tableCount];
            for (int i = 0; i < tableCount; i++) {
                tables[i] = new TableRecord {
                    Tag = reader.ReadUInt32(),
                    CheckSum = reader.ReadUInt32BE(),
                    Offset = reader.ReadUInt32BE(),
                    Length = reader.ReadUInt32BE(),
                };
            }

            return tables;
        }

        public static void ReadHead (DataReader reader, TableRecord[] tables, out FaceHeader header) {
            SeekToTable(reader, tables, FourCC.Head, required: true);

            // 'head' table contains global information for the font face
            // we only care about a few fields in it
            reader.Skip(sizeof(int) * 4);   // version, revision, checksum, magic number

            header = new FaceHeader {
                Flags = (HeadFlags)reader.ReadUInt16BE(),
                UnitsPerEm = reader.ReadUInt16BE()
            };
            if (header.UnitsPerEm == 0)
                throw new InvalidFontException("Invalid 'head' table.");

            // skip over created and modified times, bounding box,
            // deprecated style bits, direction hints, and size hints
            reader.Skip(sizeof(long) * 2 + sizeof(short) * 7);

            header.IndexFormat = (IndexFormat)reader.ReadInt16BE();
        }

        public static void ReadMaxp (DataReader reader, TableRecord[] tables, ref FaceHeader header) {
            SeekToTable(reader, tables, FourCC.Maxp, required: true);

            if (reader.ReadInt32BE() != 0x00010000)
                throw new InvalidFontException("Font contains an old style maxp table.");

            header.GlyphCount = reader.ReadUInt16BE();
            if (header.GlyphCount > MaxGlyphs)
                throw new InvalidFontException("Font contains too many glyphs.");

            // skip maxPoints, maxContours, maxCompositePoints, maxCompositeContours, maxZones
            reader.Skip(sizeof(short) * 5);

            header.MaxTwilightPoints = reader.ReadUInt16BE();
            header.MaxStorageLocations = reader.ReadUInt16BE();
            header.MaxFunctionDefs = reader.ReadUInt16BE();
            header.MaxInstructionDefs = reader.ReadUInt16BE();
            header.MaxStackSize = reader.ReadUInt16BE();

            // sanity checking
            if (header.MaxTwilightPoints > MaxTwilightPoints || header.MaxStorageLocations > MaxStorageLocations ||
                header.MaxFunctionDefs > MaxFunctionDefs || header.MaxInstructionDefs > MaxFunctionDefs ||
                header.MaxStackSize > MaxStackSize)
                throw new InvalidFontException("Font programs have limits that are larger than built-in sanity checks.");
        }

        public static MetricsHeader ReadMetricsHeader (DataReader reader) {
            // skip over version
            reader.Skip(sizeof(int));

            var header = new MetricsHeader {
                Ascender = reader.ReadInt16BE(),
                Descender = reader.ReadInt16BE(),
                LineGap = reader.ReadInt16BE()
            };

            // skip over advanceWidthMax, minLsb, minRsb, xMaxExtent, caretSlopeRise,
            // caretSlopeRun, caretOffset, 4 reserved entries, and metricDataFormat
            reader.Skip(sizeof(short) * 12);

            header.MetricCount = reader.ReadUInt16BE();
            return header;
        }

        public static MetricsEntry[] ReadMetricsTable (DataReader reader, int glyphCount, int metricCount) {
            var results = new MetricsEntry[glyphCount];
            for (int i = 0; i < metricCount; i++) {
                results[i] = new MetricsEntry {
                    Advance = reader.ReadUInt16BE(),
                    FrontSideBearing = reader.ReadInt16BE()
                };
            }

            // there might be an additional array of fsb-only entries
            var extraCount = glyphCount - metricCount;
            var lastAdvance = results[metricCount - 1].Advance;
            for (int i = 0; i < extraCount; i++) {
                results[i + metricCount] = new MetricsEntry {
                    Advance = lastAdvance,
                    FrontSideBearing = reader.ReadInt16BE()
                };
            }

            return results;
        }

        public static OS2Data ReadOS2 (DataReader reader, TableRecord[] tables) {
            SeekToTable(reader, tables, FourCC.OS_2, required: true);

            // skip over version, xAvgCharWidth
            reader.Skip(sizeof(short) * 2);

            var result = new OS2Data {
                Weight = (FontWeight)reader.ReadUInt16BE(),
                Stretch = (FontStretch)reader.ReadUInt16BE()
            };

            // skip over fsType, ySubscriptXSize, ySubscriptYSize, ySubscriptXOffset, ySubscriptYOffset,
            // ySuperscriptXSize, ySuperscriptYSize, ySuperscriptXOffset, ySuperscriptXOffset
            reader.Skip(sizeof(short) * 9);

            result.StrikeoutSize = reader.ReadInt16BE();
            result.StrikeoutPosition = reader.ReadInt16BE();

            // skip over sFamilyClass, panose[10], ulUnicodeRange1-4, achVendID[4]
            reader.Skip(sizeof(short) + sizeof(int) * 4 + 14);

            // check various style flags
            var fsSelection = (FsSelectionFlags)reader.ReadUInt16BE();
            result.Style = (fsSelection & FsSelectionFlags.Italic) != 0 ? FontStyle.Italic :
                            (fsSelection & FsSelectionFlags.Bold) != 0 ? FontStyle.Bold :
                            (fsSelection & FsSelectionFlags.Oblique) != 0 ? FontStyle.Oblique :
                            FontStyle.Regular;
            result.IsWWSFont = (fsSelection & FsSelectionFlags.WWS) != 0;
            result.UseTypographicMetrics = (fsSelection & FsSelectionFlags.UseTypoMetrics) != 0;

            // skip over usFirstCharIndex, usLastCharIndex
            reader.Skip(sizeof(short) * 2);

            result.TypographicAscender = reader.ReadInt16BE();
            result.TypographicDescender = reader.ReadInt16BE();
            result.TypographicLineGap = reader.ReadInt16BE();
            result.WinAscent = reader.ReadUInt16BE();
            result.WinDescent = reader.ReadUInt16BE();

            // skip over ulCodePageRange1-2
            reader.Skip(sizeof(int) * 2);

            result.XHeight = reader.ReadInt16BE();
            result.CapHeight = reader.ReadInt16BE();

            return result;
        }

        public static void ReadPost (DataReader reader, TableRecord[] tables, ref FaceHeader header) {
            if (!SeekToTable(reader, tables, FourCC.Post))
                return;

            // skip over version and italicAngle
            reader.Skip(sizeof(int) * 2);

            header.UnderlinePosition = reader.ReadInt16BE();
            header.UnderlineThickness = reader.ReadInt16BE();
            header.IsFixedPitch = reader.ReadUInt32BE() != 0;
        }

        public static void ReadLoca (DataReader reader, TableRecord[] tables, IndexFormat format, uint* table, int count) {
            SeekToTable(reader, tables, FourCC.Loca, required: true);

            if (format == IndexFormat.Short) {
                // values are ushort, divided by 2, so we need to shift back
                for (int i = 0; i < count; i++)
                    *table++ = (uint)(reader.ReadUInt16BE() << 1);
            }
            else {
                for (int i = 0; i < count; i++)
                    *table++ = reader.ReadUInt32BE();
            }
        }

        public unsafe static NameData ReadNames (DataReader reader, TableRecord[] tables) {
            if (!SeekToTable(reader, tables, FourCC.Name))
                return default(NameData);

            // read header
            var currentOffset = reader.Position;
            var format = reader.ReadUInt16BE();
            var count = reader.ReadUInt16BE();
            var dataOffset = currentOffset + reader.ReadUInt16BE();

            // read name records, filtering out non-Unicode and platforms we don't know about
            var stringData = stackalloc StringData[count];
            var stringDataCount = 0;
            for (int i = 0; i < count; i++) {
                var platform = reader.ReadUInt16BE();
                var encoding = reader.ReadUInt16BE();
                var language = reader.ReadUInt16BE();
                var name = reader.ReadUInt16BE();
                var length = reader.ReadUInt16BE();
                var offset = reader.ReadUInt16BE();

                // we only support Unicode strings
                if (platform == PlatformID.Microsoft) {
                    if (encoding != WindowsEncoding.UnicodeBmp && encoding != WindowsEncoding.UnicodeFull)
                        continue;

                    //if (language != CultureInfo.CurrentCulture.LCID)
                        //continue;
                }
                else if (platform != PlatformID.Unicode)
                    continue;

                stringData[stringDataCount++] = new StringData {
                    Name = name,
                    Offset = offset,
                    Length = length
                };
            }

            // find strings we care about and extract them from the blob
            var nameData = new NameData();
            for (int i = 0; i < stringDataCount; i++) {
                var data = stringData[i];
                switch (data.Name) {
                    case NameID.FamilyName: nameData.FamilyName = ExtractString(reader, dataOffset, data); break;
                    case NameID.SubfamilyName: nameData.SubfamilyName = ExtractString(reader, dataOffset, data); break;
                    case NameID.UniqueID: nameData.UniqueID = ExtractString(reader, dataOffset, data); break;
                    case NameID.FullName: nameData.FullName = ExtractString(reader, dataOffset, data); break;
                    case NameID.Version: nameData.Version = ExtractString(reader, dataOffset, data); break;
                    case NameID.Description: nameData.Description = ExtractString(reader, dataOffset, data); break;
                    case NameID.TypographicFamilyName: nameData.TypographicFamilyName = ExtractString(reader, dataOffset, data); break;
                    case NameID.TypographicSubfamilyName: nameData.TypographicSubfamilyName = ExtractString(reader, dataOffset, data); break;
                }
            }

            return nameData;
        }

        public static FUnit[] ReadCvt (DataReader reader, TableRecord[] tables) {
            var index = FindTable(tables, FourCC.Cvt);
            if (index == -1)
                return null;

            reader.Seek(tables[index].Offset);

            var results = new FUnit[tables[index].Length / sizeof(short)];
            for (int i = 0; i < results.Length; i++)
                results[i] = (FUnit)reader.ReadInt16BE();

            return results;
        }

        public static byte[] ReadProgram (DataReader reader, TableRecord[] tables, FourCC tag) {
            var index = FindTable(tables, tag);
            if (index == -1)
                return null;

            reader.Seek(tables[index].Offset);
            return reader.ReadBytes((int)tables[index].Length);
        }

        public static int FindTable (TableRecord[] tables, FourCC tag) {
            var index = -1;
            for (int i = 0; i < tables.Length; i++) {
                if (tables[i].Tag == tag) {
                    index = i;
                    break;
                }
            }

            return index;
        }

        public static bool SeekToTable (DataReader reader, TableRecord[] tables, FourCC tag, bool required = false) {
            // check if we have the desired table and that it's not empty
            var index = FindTable(tables, tag);
            if (index == -1 || tables[index].Length == 0) {
                if (required)
                    throw new InvalidFontException($"Missing or empty '{tag}' table.");
                return false;
            }

            // seek to the appropriate offset
            reader.Seek(tables[index].Offset);
            return true;
        }

        public static void ReadGlyph (
            DataReader reader, int glyphIndex, int recursionDepth,
            BaseGlyph[] glyphTable, uint glyfOffset, uint glyfLength, uint* loca
        ) {
            // check if this glyph has already been loaded; this can happen
            // if we're recursively loading subglyphs as part of a composite
            if (glyphTable[glyphIndex] != null)
                return;

            // prevent bad font data from causing infinite recursion
            if (recursionDepth > MaxRecursion)
                throw new InvalidFontException("Bad font data; infinite composite recursion.");

            // check if this glyph doesn't have any actual data
            GlyphHeader header;
            var offset = loca[glyphIndex];
            if ((glyphIndex < glyphTable.Length - 1 && offset == loca[glyphIndex + 1]) || offset >= glyfLength) {
                // this is an empty glyph, so synthesize a header
                header = default(GlyphHeader);
            }
            else {
                // seek to the right spot and load the header
                reader.Seek(glyfOffset + loca[glyphIndex]);
                header = new GlyphHeader {
                    ContourCount = reader.ReadInt16BE(),
                    MinX = reader.ReadInt16BE(),
                    MinY = reader.ReadInt16BE(),
                    MaxX = reader.ReadInt16BE(),
                    MaxY = reader.ReadInt16BE()
                };

                if (header.ContourCount < -1 || header.ContourCount > MaxContours)
                    throw new InvalidFontException("Invalid number of contours for glyph.");
            }

            if (header.ContourCount > 0) {
                // positive contours means a simple glyph
                glyphTable[glyphIndex] = ReadSimpleGlyph(reader, header.ContourCount);
            }
            else if (header.ContourCount == -1) {
                // -1 means composite glyph
                var composite = ReadCompositeGlyph(reader);
                var subglyphs = composite.Subglyphs;

                // read each subglyph recrusively
                for (int i = 0; i < subglyphs.Length; i++)
                    ReadGlyph(reader, subglyphs[i].Index, recursionDepth + 1, glyphTable, glyfOffset, glyfLength, loca);

                glyphTable[glyphIndex] = composite;
            }
            else {
                // no data, so synthesize an empty glyph
                glyphTable[glyphIndex] = new SimpleGlyph {
                    Points = new Point[0],
                    ContourEndpoints = new int[0]
                };
            }

            // save bounding box
            var glyph = glyphTable[glyphIndex];
            glyph.MinX = header.MinX;
            glyph.MinY = header.MinY;
            glyph.MaxX = header.MaxX;
            glyph.MaxY = header.MaxY;
        }

        static SimpleGlyph ReadSimpleGlyph (DataReader reader, int contourCount) {
            // read contour endpoints
            var contours = new int[contourCount];
            var lastEndpoint = reader.ReadUInt16BE();
            contours[0] = lastEndpoint;
            for (int i = 1; i < contours.Length; i++) {
                var endpoint = reader.ReadUInt16BE();
                contours[i] = endpoint;
                if (contours[i] <= lastEndpoint)
                    throw new InvalidFontException("Glyph contour endpoints are unordered.");

                lastEndpoint = endpoint;
            }

            // the last contour's endpoint is the number of points in the glyph
            var pointCount = lastEndpoint + 1;
            var points = new Point[pointCount];

            // read instruction data
            var instructionLength = reader.ReadUInt16BE();
            var instructions = reader.ReadBytes(instructionLength);

            // read flags
            var flags = new SimpleGlyphFlags[pointCount];
            int flagIndex = 0;
            while (flagIndex < flags.Length) {
                var f = (SimpleGlyphFlags)reader.ReadByte();
                flags[flagIndex++] = f;

                // if Repeat is set, this flag data is repeated n more times
                if ((f & SimpleGlyphFlags.Repeat) != 0) {
                    var count = reader.ReadByte();
                    for (int i = 0; i < count; i++)
                        flags[flagIndex++] = f;
                }
            }

            // Read points, first doing all X coordinates and then all Y coordinates.
            // The point packing is insane; coords are either 1 byte or 2; they're
            // deltas from previous point, and flags let you repeat identical points.
            var x = 0;
            for (int i = 0; i < points.Length; i++) {
                var f = flags[i];
                var delta = 0;

                if ((f & SimpleGlyphFlags.ShortX) != 0) {
                    delta = reader.ReadByte();
                    if ((f & SimpleGlyphFlags.SameX) == 0)
                        delta = -delta;
                }
                else if ((f & SimpleGlyphFlags.SameX) == 0)
                    delta = reader.ReadInt16BE();

                x += delta;
                points[i].X = (FUnit)x;
            }

            var y = 0;
            for (int i = 0; i < points.Length; i++) {
                var f = flags[i];
                var delta = 0;

                if ((f & SimpleGlyphFlags.ShortY) != 0) {
                    delta = reader.ReadByte();
                    if ((f & SimpleGlyphFlags.SameY) == 0)
                        delta = -delta;
                }
                else if ((f & SimpleGlyphFlags.SameY) == 0)
                    delta = reader.ReadInt16BE();

                y += delta;
                points[i].Y = (FUnit)y;
                points[i].Type = (f & SimpleGlyphFlags.OnCurve) != 0 ? PointType.OnCurve : PointType.Quadratic;
            }

            return new SimpleGlyph {
                Points = points,
                ContourEndpoints = contours,
                Instructions = instructions
            };
        }

        static CompositeGlyph ReadCompositeGlyph (DataReader reader) {
            // we need to keep reading glyphs for as long as
            // our flags tell us that there are more to read
            var subglyphs = new List<Subglyph>();

            CompositeGlyphFlags flags;
            do {
                flags = (CompositeGlyphFlags)reader.ReadUInt16BE();

                var subglyph = new Subglyph { Flags = flags };
                subglyph.Index = reader.ReadUInt16BE();

                // read in args; they vary in size based on flags
                if ((flags & CompositeGlyphFlags.ArgsAreWords) != 0) {
                    subglyph.Arg1 = reader.ReadInt16BE();
                    subglyph.Arg2 = reader.ReadInt16BE();
                }
                else {
                    subglyph.Arg1 = reader.ReadSByte();
                    subglyph.Arg2 = reader.ReadSByte();
                }

                // figure out the transform; we can either have no scale, a uniform
                // scale, two independent scales, or a full 2x2 transform matrix
                // transform components are in 2.14 fixed point format
                var transform = Matrix3x2.Identity;
                if ((flags & CompositeGlyphFlags.HaveScale) != 0) {
                    var scale = reader.ReadInt16BE() / F2Dot14ToFloat;
                    transform.M11 = scale;
                    transform.M22 = scale;
                }
                else if ((flags & CompositeGlyphFlags.HaveXYScale) != 0) {
                    transform.M11 = reader.ReadInt16BE() / F2Dot14ToFloat;
                    transform.M22 = reader.ReadInt16BE() / F2Dot14ToFloat;
                }
                else if ((flags & CompositeGlyphFlags.HaveTransform) != 0) {
                    transform.M11 = reader.ReadInt16BE() / F2Dot14ToFloat;
                    transform.M12 = reader.ReadInt16BE() / F2Dot14ToFloat;
                    transform.M21 = reader.ReadInt16BE() / F2Dot14ToFloat;
                    transform.M22 = reader.ReadInt16BE() / F2Dot14ToFloat;
                }

                subglyph.Transform = transform;
                subglyphs.Add(subglyph);

            } while ((flags & CompositeGlyphFlags.MoreComponents) != 0);

            var result = new CompositeGlyph { Subglyphs = subglyphs.ToArray() };

            // if we have instructions, read them now
            if ((flags & CompositeGlyphFlags.HaveInstructions) != 0) {
                var instructionLength = reader.ReadUInt16BE();
                result.Instructions = reader.ReadBytes(instructionLength);
            }

            return result;
        }

        static string ExtractString (DataReader reader, uint baseOffset, StringData data) {
            reader.Seek(baseOffset + data.Offset);

            var bytes = reader.ReadBytes(data.Length);
            return Encoding.BigEndianUnicode.GetString(bytes);
        }

        // most of these limits are arbitrary; they can be increased if you
        // run into a font in the wild that is constrained by them
        const uint TTFv1 = 0x10000;
        const uint TTFv2 = 0x20000;
        const int MaxGlyphs = short.MaxValue;
        const int MaxContours = 256;
        const int MaxRecursion = 128;
        const int MaxFontsInCollection = 64;
        const int MaxStackSize = 16384;
        const int MaxTwilightPoints = short.MaxValue;
        const int MaxFunctionDefs = 4096;
        const int MaxStorageLocations = 16384;
        const float F2Dot14ToFloat = 16384.0f;

        [Flags]
        enum SimpleGlyphFlags {
            None = 0,
            OnCurve = 0x1,
            ShortX = 0x2,
            ShortY = 0x4,
            Repeat = 0x8,
            SameX = 0x10,
            SameY = 0x20
        }

        [Flags]
        enum FsSelectionFlags {
            Italic = 0x1,
            Bold = 0x20,
            Regular = 0x40,
            UseTypoMetrics = 0x80,
            WWS = 0x100,
            Oblique = 0x200
        }

        struct GlyphHeader {
            public short ContourCount;
            public short MinX;
            public short MinY;
            public short MaxX;
            public short MaxY;
        }

        struct StringData {
            public ushort Name;
            public ushort Offset;
            public ushort Length;
        }

        static class NameID {
            public const int FamilyName = 1;
            public const int SubfamilyName = 2;
            public const int UniqueID = 3;
            public const int FullName = 4;
            public const int Version = 5;
            public const int Description = 10;
            public const int TypographicFamilyName = 16;
            public const int TypographicSubfamilyName = 17;
        }
    }

    struct TableRecord {
        public FourCC Tag;
        public uint CheckSum;
        public uint Offset;
        public uint Length;

        public override string ToString () => Tag.ToString();
    }

    struct FaceHeader {
        public HeadFlags Flags;
        public int UnitsPerEm;
        public IndexFormat IndexFormat;
        public int UnderlinePosition;
        public int UnderlineThickness;
        public bool IsFixedPitch;
        public int GlyphCount;
        public int MaxTwilightPoints;
        public int MaxStorageLocations;
        public int MaxFunctionDefs;
        public int MaxInstructionDefs;
        public int MaxStackSize;
    }

    struct MetricsHeader {
        public int Ascender;
        public int Descender;
        public int LineGap;
        public int MetricCount;
    }

    struct MetricsEntry {
        public int Advance;
        public int FrontSideBearing;
    }

    struct OS2Data {
        public FontWeight Weight;
        public FontStretch Stretch;
        public FontStyle Style;
        public int StrikeoutSize;
        public int StrikeoutPosition;
        public int TypographicAscender;
        public int TypographicDescender;
        public int TypographicLineGap;
        public int WinAscent;
        public int WinDescent;
        public bool UseTypographicMetrics;
        public bool IsWWSFont;
        public int XHeight;
        public int CapHeight;
    }

    struct NameData {
        public string FamilyName;
        public string SubfamilyName;
        public string UniqueID;
        public string FullName;
        public string Version;
        public string Description;
        public string TypographicFamilyName;
        public string TypographicSubfamilyName;
    }

    abstract class BaseGlyph {
        public byte[] Instructions;
        public int MinX;
        public int MinY;
        public int MaxX;
        public int MaxY;
    }

    class SimpleGlyph : BaseGlyph {
        public Point[] Points;
        public int[] ContourEndpoints;
    }

    struct Subglyph {
        public Matrix3x2 Transform;
        public CompositeGlyphFlags Flags;
        public int Index;
        public int Arg1;
        public int Arg2;
    }

    class CompositeGlyph : BaseGlyph {
        public Subglyph[] Subglyphs;
    }

    static class PlatformID {
        public const int Unicode = 0;
        public const int Microsoft = 3;
    }

    static class WindowsEncoding {
        public const int UnicodeBmp = 1;
        public const int UnicodeFull = 10;
    }

    static class UnicodeEncoding {
        public const int Unicode32 = 4;
    }

    enum IndexFormat {
        Short,
        Long
    }

    [Flags]
    enum CompositeGlyphFlags {
        None = 0,
        ArgsAreWords = 0x1,
        ArgsAreXYValues = 0x2,
        RoundXYToGrid = 0x4,
        HaveScale = 0x8,
        MoreComponents = 0x20,
        HaveXYScale = 0x40,
        HaveTransform = 0x80,
        HaveInstructions = 0x100,
        UseMetrics = 0x200,
        ScaledComponentOffset = 0x800
    }

    [Flags]
    enum HeadFlags {
        None = 0,
        SimpleBaseline = 0x1,
        SimpleLsb = 0x2,
        SizeDependentInstructions = 0x4,
        IntegerPpem = 0x8,
        InstructionsAlterAdvance = 0x10
    }

    // helper wrapper around 4CC codes for debugging purposes
    struct FourCC {
        uint value;

        public FourCC (uint value) {
            this.value = value;
        }

        public FourCC (string str) {
            if (str.Length != 4)
                throw new InvalidOperationException("Invalid FourCC code");
            value = str[0] | ((uint)str[1] << 8) | ((uint)str[2] << 16) | ((uint)str[3] << 24);
        }

        public override string ToString () {
            return new string(new[] {
                    (char)(value & 0xff),
                    (char)((value >> 8) & 0xff),
                    (char)((value >> 16) & 0xff),
                    (char)(value >> 24)
                });
        }

        public static implicit operator FourCC (string value) => new FourCC(value);
        public static implicit operator FourCC (uint value) => new FourCC(value);
        public static implicit operator uint (FourCC fourCC) => fourCC.value;

        public static readonly FourCC Otto = "OTTO";
        public static readonly FourCC True = "true";
        public static readonly FourCC Ttcf = "ttcf";
        public static readonly FourCC Typ1 = "typ1";
        public static readonly FourCC Head = "head";
        public static readonly FourCC Maxp = "maxp";
        public static readonly FourCC Post = "post";
        public static readonly FourCC OS_2 = "OS/2";
        public static readonly FourCC Hhea = "hhea";
        public static readonly FourCC Hmtx = "hmtx";
        public static readonly FourCC Vhea = "vhea";
        public static readonly FourCC Vmtx = "vmtx";
        public static readonly FourCC Loca = "loca";
        public static readonly FourCC Glyf = "glyf";
        public static readonly FourCC Cmap = "cmap";
        public static readonly FourCC Kern = "kern";
        public static readonly FourCC Name = "name";
        public static readonly FourCC Cvt = "cvt ";
        public static readonly FourCC Fpgm = "fpgm";
        public static readonly FourCC Prep = "prep";
        public static readonly FourCC Eblc = "EBLC";
    }
}


text:
<?php

namespace Magento\Downloadable\Model\Product\TypeHandler;

use Magento\Catalog\Model\Product;

/**
 * Interface TypeHandlerInterface
 */
interface TypeHandlerInterface
{
    /**
     * @param Product $product
     * @param array $data
     * @return void
     */
    public function save(Product $product, array $data);

    /**
     * @param array $data
     * @return bool
     */
    public function isCanHandle(array $data);
}


text:
'''
Created on 28 Jun 2014

@author: heiko
'''
import numpy as np
from seizures.evaluation.XValidation import XValidation
from seizures.prediction.ForestPredictor import ForestPredictor
from seizures.prediction.RandomPredictor import RandomPredictor
from seizures.prediction.SVMPredictor import SVMPredictor

def test_predictor(predictor_cls):
    predictor = predictor_cls()

    N=1000
    D=2
    # simulate a 2-fold cross validation
    Xs = [np.random.randn(N/2, D), np.random.randn(N/2, D)]
    ys = [np.random.randint(0, 2, N/2), np.random.randint(0, 2, N/2)]
    #X=np.random.randn(N,D)
    #y=np.random.randint(0,2,N)

    print XValidation.evaluate(Xs, ys, predictor)

if __name__ == '__main__':
    print "RandomPredictor"
    test_predictor(RandomPredictor)

    print "ForestPredictor"
    test_predictor(ForestPredictor)

    print "SVMPredictor"
    test_predictor(SVMPredictor)


text:
azure.mgmt.datalake.analytics.catalog package
=============================================

Submodules
----------

.. toctree::

   azure.mgmt.datalake.analytics.catalog.models
   azure.mgmt.datalake.analytics.catalog.operations

Module contents
---------------

.. automodule:: azure.mgmt.datalake.analytics.catalog
    :members:
    :undoc-members:
    :show-inheritance:


text:
package ru.course.selenium.pages.admin.settings;

import org.openqa.selenium.WebDriver;
import ru.course.selenium.pages.admin.BaseAdminPage;

/**
 * Created by Aleksei.Klimenko on 28.11.2016.
 */
public class SecurityPage extends BaseAdminPage {
    public static final String PAGE_TITLE_LOCAL = "Settings";
    public static final String LOCAL_URL = "/admin/?app=settings&doc=security";

    public SecurityPage(WebDriver driver) {
        super(driver);
    }

    @Override
    public String getPageTitleExpected() {
        return PAGE_TITLE_LOCAL;
    }

    @Override
    protected String getPageUrlLocal() {
        return LOCAL_URL;
    }
}


text:
namespace boost {
namespace archive {

typedef detail::polymorphic_oarchive_route<
        xml_woarchive_impl<naked_xml_woarchive>
> polymorphic_xml_woarchive;

} // namespace archive
} // namespace boost

// required by export
BOOST_SERIALIZATION_REGISTER_ARCHIVE(
    boost::archive::polymorphic_xml_woarchive
)

#endif // BOOST_NO_STD_WSTREAMBUF
#endif // BOOST_ARCHIVE_POLYMORPHIC_XML_WOARCHIVE_HPP


text:
#ifndef TATO_SSOCONTROLLERS_WEBS_ADMINS_H
#define TATO_SSOCONTROLLERS_WEBS_ADMINS_H

#include <cppcms_skel/controllers/webs/Controller.h>

//needed to avoid to include models directly here,
//which would have the side effects to require to recompile the
// controller every time we modify a model. even though it does
// not affect the controller
namespace tatosso {

namespace models {
    // %%%NEXT_CLASS_MODEL_CTRL_MARKER%%%
}

namespace controllers {
namespace webs {
/**
 * @class Admins module to centralize administrator actions
 * @since 31 March 2014
 */
class Admins : public ::controllers::webs::Controller {
    public:
        /**
         * @brief Constructor
         * @since 31 March 2014
         */
        Admins(cppcms::service &serv);

        /**
         * @brief Destructor
         * @since 31 March 2014
         */
        ~Admins();

    private:
        // %%%NEXT_VAR_MODEL_CTRL_MARKER%%%

        /**
         * @TODO
         * @since 31 March 2014
         */
        void reset_password();

        /**
         * @TODO
         * @since 31 March 2014
         */
        void reset_password_treat();

// %%%NEXT_ACTION_MARKER%%% , do not delete

};

} // End namespace webs
} // End namespace controllers
} // End namespace tatosso

#endif


text:
package io.astefanutti.metrics.cdi.se;

import org.eclipse.microprofile.metrics.annotation.Counted;
import org.eclipse.microprofile.metrics.annotation.Metered;
import org.eclipse.microprofile.metrics.annotation.Timed;

public class MultipleMetricsConstructorBean {

    @Counted(name = "counter", monotonic = true)
    @Metered(name = "meter")
    @Timed(name = "timer")
    public MultipleMetricsConstructorBean() {
    }
}


text:
import Markdown from './Markdown';
export default Markdown;


text:
import React from 'react'
import Terminal from './Terminal'
import styled from 'styled-components'

const Container = styled.div`
  position: relative;
  height: 350px;
  width: 80%;
  margin: auto;
  left: -4%;

  @media screen and (min-width: ${(props) => props.theme.breakpoints.TABLET}) {
    height: 400px;
  }
`

class Windows extends React.Component {
  constructor (props) {
    super(props)
    this.state = {
      showTopTerminal: true,
      showMiddleTerminal: true,
      showBottomTerminal: true,
      counter: 0
    }
    this.onHide = this.onHide.bind(this)
  }

  onHide (terminal) {
    this.setState({ [terminal]: false, counter: this.state.counter + 1 }, () => {
      if (this.state.counter === 3) {
        this.setState({
          showTopTerminal: true,
          showMiddleTerminal: true,
          showBottomTerminal: true,
          counter: 0
        })
      }
    })
  }

  render () {
    return (
      <Container>
        {this.state.showTopTerminal &&
          <Terminal
            onClose={() => this.onHide('showTopTerminal')}
            top={'0%'}
            left={'0%'}
          />
        }

        {this.state.showMiddleTerminal &&
          <Terminal
            onClose={() => this.onHide('showMiddleTerminal')}
            top={'8%'}
            left={'5%'}
          />
        }

        {this.state.showBottomTerminal &&
          <Terminal
            onClose={() => this.onHide('showBottomTerminal')}
            top={'16%'}
            left={'10%'}
          />
        }
      </Container>
    )
  }
}

export default Windows


text:
<?php

// PortfolioPortfolioBundle:Portfolio:articles.html.twig
return array (
);


text:
package org.apache.syncope.core.provisioning.api.notification;

import java.util.Set;
import org.apache.syncope.core.persistence.api.entity.Notification;

@FunctionalInterface
public interface RecipientsProvider {

    Set<String> provideRecipients(Notification notification);
}


text:
// Type definitions for Umzug v2.1.0
// Project: https://github.com/sequelize/umzug
// Definitions by: Ivan Drinchev <https://github.com/drinchev>
//                 Margus Lamp <https://github.com/mlamp>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.3

import { EventEmitter } from 'events';
import Sequelize = require("sequelize");

declare namespace umzug {

    interface MigrationOptions {

        /*
         * The params that gets passed to the migrations.
         * Might be an array or a synchronous function which returns an array.
         */
        params?: Array<any>;

        /** The path to the migrations directory. */
        path?: string;

        /** The pattern that determines whether or not a file is a migration. */
        pattern?: RegExp;

        /**
         * A function that receives and returns the to be executed function.
         * This can be used to modify the function.
         */
        wrap?: <T>(fn: T) => T;

        /**
         * A function that maps a file path to a migration object in the form
         * { up: Function, down: Function }. The default for this is to require(...)
         * the file as javascript, but you can use this to transpile TypeScript,
         * read raw sql etc.
         * See https://github.com/sequelize/umzug/tree/master/test/fixtures
         * for examples.
         */
        customResolver?(path: string): { up: () => Promise<any>, down?: () => Promise<any> };

    }

    interface JSONStorageOptions {

        /**
         * The path to the json storage.
         * Defaults to process.cwd() + '/umzug.json';
         */
        path?: string;

    }

    interface SequelizeStorageOptions {

        /**
         * The configured instance of Sequelize.
         * Optional if `model` is passed.
         */
        sequelize?: Sequelize.Sequelize;

        /**
         * The to be used Sequelize model.
         * Must have column name matching `columnName` option
         * Optional of `sequelize` is passed.
         */
        model?: Sequelize.Model<any, any>;

        /**
         * The name of the to be used model.
         * Defaults to 'SequelizeMeta'
         */
        modelName?: string;

        /**
         * The name of table to create if `model` option is not supplied
         * Defaults to `modelName`
         */
        tableName?: string;

        /**
         * The name of table column holding migration name.
         * Defaults to 'name'.
         */
        columnName: string;

        /**
         * The type of the column holding migration name.
         * Defaults to `Sequelize.STRING`
         */
        columnType: Sequelize.DataTypeAbstract;

    }

    interface ExecuteOptions {
        migrations?: Array<string>;
        method?: string;
    }

    interface UmzugOptions {

        /**
         * The storage.
         * Possible values: 'json', 'sequelize', an object
         */
        storage?: string;

        /**
         * The options for the storage.
         */
        storageOptions?: JSONStorageOptions | SequelizeStorageOptions | Object;

        /**
         * The logging function.
         * A function that gets executed everytime migrations start and have ended.
         */
        logging?: boolean | Function;

        /**
         * The name of the positive method in migrations.
         */
        upName?: string;

        /**
         * The name of the negative method in migrations.
         */
        downName?: string;

        /**
         * Options for defined migration
         */
        migrations?: MigrationOptions;

    }

    interface UpToOptions {

        /**
         * It is also possible to pass the name of a migration in order to
         * just run the migrations from the current state to the passed
         * migration name.
         */
        to: string;

    }

    interface DownToOptions {

        /**
         * It is also possible to pass the name of a migration in order to
         * just run the migrations from the current state to the passed
         * migration name. down allows to pass 0 to revert everything.
         */
        to: string | 0;

    }

    interface UpDownMigrationsOptions {

        /**
         * Running specific migrations while ignoring the right order, can be
         * done like this:
         */
        migrations: Array<string>;

    }

    interface Migration {
        path: string;
        file: string;
    }

    interface Umzug extends EventEmitter {
        /**
         * The execute method is a general purpose function that runs for
         * every specified migrations the respective function.
         */
        execute(options?: ExecuteOptions): Promise<Migration[]>;

        /**
         * You can get a list of pending/not yet executed migrations like this:
         */
        pending(): Promise<Migration[]>;

        /**
         * You can get a list of already executed migrations like this:
         */
        executed(): Promise<Migration[]>;

        /**
         * The up method can be used to execute all pending migrations.
         */
        up(migration?: string): Promise<Migration[]>;
        up(migrations?: string[]): Promise<Migration[]>;
        up(options?: UpToOptions | UpDownMigrationsOptions): Promise<Migration[]>;

        /**
         * The down method can be used to revert the last executed migration.
         */
        down(migration?: string): Promise<Migration[]>;
        down(migrations?: string[]): Promise<Migration[]>;
        down(options?: DownToOptions | UpDownMigrationsOptions): Promise<Migration[]>;

        on(eventName: 'migrating' | 'reverting' | 'migrated' | 'reverted', cb?: (name: string, migration: Migration) => void): this;
        addListener(eventName: 'migrating' | 'reverting' | 'migrated' | 'reverted', cb?: (name: string, migration: Migration) => void): this;
        removeListener(eventName: 'migrating' | 'reverting' | 'migrated' | 'reverted', cb?: (name: string, migration: Migration) => void): this;

    }

    interface UmzugStatic {
        new(options?: UmzugOptions): Umzug;
    }
}

declare const umzug: umzug.UmzugStatic;
export = umzug;


text:
import errno
import os
import signal
import socket
from subprocess import Popen
import sys
import time
import unittest

from tornado.netutil import (
    BlockingResolver,
    OverrideResolver,
    ThreadedResolver,
    is_valid_ip,
    bind_sockets,
)
from tornado.testing import AsyncTestCase, gen_test, bind_unused_port
from tornado.test.util import skipIfNoNetwork

import typing

if typing.TYPE_CHECKING:
    from typing import List  # noqa: F401

try:
    import pycares  # type: ignore
except ImportError:
    pycares = None
else:
    from tornado.platform.caresresolver import CaresResolver

try:
    import twisted  # type: ignore
    import twisted.names  # type: ignore
except ImportError:
    twisted = None
else:
    from tornado.platform.twisted import TwistedResolver

class _ResolverTestMixin(object):
    @gen_test
    def test_localhost(self):
        addrinfo = yield self.resolver.resolve("localhost", 80, socket.AF_UNSPEC)
        self.assertIn((socket.AF_INET, ("127.0.0.1", 80)), addrinfo)

# It is impossible to quickly and consistently generate an error in name
# resolution, so test this case separately, using mocks as needed.
class _ResolverErrorTestMixin(object):
    @gen_test
    def test_bad_host(self):
        with self.assertRaises(IOError):
            yield self.resolver.resolve("an invalid domain", 80, socket.AF_UNSPEC)

def _failing_getaddrinfo(*args):
    """Dummy implementation of getaddrinfo for use in mocks"""
    raise socket.gaierror(errno.EIO, "mock: lookup failed")

@skipIfNoNetwork
class BlockingResolverTest(AsyncTestCase, _ResolverTestMixin):
    def setUp(self):
        super(BlockingResolverTest, self).setUp()
        self.resolver = BlockingResolver()

# getaddrinfo-based tests need mocking to reliably generate errors;
# some configurations are slow to produce errors and take longer than
# our default timeout.
class BlockingResolverErrorTest(AsyncTestCase, _ResolverErrorTestMixin):
    def setUp(self):
        super(BlockingResolverErrorTest, self).setUp()
        self.resolver = BlockingResolver()
        self.real_getaddrinfo = socket.getaddrinfo
        socket.getaddrinfo = _failing_getaddrinfo

    def tearDown(self):
        socket.getaddrinfo = self.real_getaddrinfo
        super(BlockingResolverErrorTest, self).tearDown()

class OverrideResolverTest(AsyncTestCase, _ResolverTestMixin):
    def setUp(self):
        super(OverrideResolverTest, self).setUp()
        mapping = {
            ("google.com", 80): ("1.2.3.4", 80),
            ("google.com", 80, socket.AF_INET): ("1.2.3.4", 80),
            ("google.com", 80, socket.AF_INET6): (
                "2a02:6b8:7c:40c:c51e:495f:e23a:3",
                80,
            ),
        }
        self.resolver = OverrideResolver(BlockingResolver(), mapping)

    @gen_test
    def test_resolve_multiaddr(self):
        result = yield self.resolver.resolve("google.com", 80, socket.AF_INET)
        self.assertIn((socket.AF_INET, ("1.2.3.4", 80)), result)

        result = yield self.resolver.resolve("google.com", 80, socket.AF_INET6)
        self.assertIn(
            (socket.AF_INET6, ("2a02:6b8:7c:40c:c51e:495f:e23a:3", 80, 0, 0)), result
        )

@skipIfNoNetwork
class ThreadedResolverTest(AsyncTestCase, _ResolverTestMixin):
    def setUp(self):
        super(ThreadedResolverTest, self).setUp()
        self.resolver = ThreadedResolver()

    def tearDown(self):
        self.resolver.close()
        super(ThreadedResolverTest, self).tearDown()

class ThreadedResolverErrorTest(AsyncTestCase, _ResolverErrorTestMixin):
    def setUp(self):
        super(ThreadedResolverErrorTest, self).setUp()
        self.resolver = BlockingResolver()
        self.real_getaddrinfo = socket.getaddrinfo
        socket.getaddrinfo = _failing_getaddrinfo

    def tearDown(self):
        socket.getaddrinfo = self.real_getaddrinfo
        super(ThreadedResolverErrorTest, self).tearDown()

@skipIfNoNetwork
@unittest.skipIf(sys.platform == "win32", "preexec_fn not available on win32")
class ThreadedResolverImportTest(unittest.TestCase):
    def test_import(self):
        TIMEOUT = 5

        # Test for a deadlock when importing a module that runs the
        # ThreadedResolver at import-time. See resolve_test.py for
        # full explanation.
        command = [sys.executable, "-c", "import tornado.test.resolve_test_helper"]

        start = time.time()
        popen = Popen(command, preexec_fn=lambda: signal.alarm(TIMEOUT))
        while time.time() - start < TIMEOUT:
            return_code = popen.poll()
            if return_code is not None:
                self.assertEqual(0, return_code)
                return  # Success.
            time.sleep(0.05)

        self.fail("import timed out")

# We do not test errors with CaresResolver:
# Some DNS-hijacking ISPs (e.g. Time Warner) return non-empty results
# with an NXDOMAIN status code.  Most resolvers treat this as an error;
# C-ares returns the results, making the "bad_host" tests unreliable.
# C-ares will try to resolve even malformed names, such as the
# name with spaces used in this test.
@skipIfNoNetwork
@unittest.skipIf(pycares is None, "pycares module not present")
class CaresResolverTest(AsyncTestCase, _ResolverTestMixin):
    def setUp(self):
        super(CaresResolverTest, self).setUp()
        self.resolver = CaresResolver()

# TwistedResolver produces consistent errors in our test cases so we
# could test the regular and error cases in the same class. However,
# in the error cases it appears that cleanup of socket objects is
# handled asynchronously and occasionally results in "unclosed socket"
# warnings if not given time to shut down (and there is no way to
# explicitly shut it down). This makes the test flaky, so we do not
# test error cases here.
@skipIfNoNetwork
@unittest.skipIf(twisted is None, "twisted module not present")
@unittest.skipIf(
    getattr(twisted, "__version__", "0.0") < "12.1", "old version of twisted"
)
class TwistedResolverTest(AsyncTestCase, _ResolverTestMixin):
    def setUp(self):
        super(TwistedResolverTest, self).setUp()
        self.resolver = TwistedResolver()

class IsValidIPTest(unittest.TestCase):
    def test_is_valid_ip(self):
        self.assertTrue(is_valid_ip("127.0.0.1"))
        self.assertTrue(is_valid_ip("4.4.4.4"))
        self.assertTrue(is_valid_ip("::1"))
        self.assertTrue(is_valid_ip("2620:0:1cfe:face:b00c::3"))
        self.assertTrue(not is_valid_ip("www.google.com"))
        self.assertTrue(not is_valid_ip("localhost"))
        self.assertTrue(not is_valid_ip("4.4.4.4<"))
        self.assertTrue(not is_valid_ip(" 127.0.0.1"))
        self.assertTrue(not is_valid_ip(""))
        self.assertTrue(not is_valid_ip(" "))
        self.assertTrue(not is_valid_ip("\n"))
        self.assertTrue(not is_valid_ip("\x00"))

class TestPortAllocation(unittest.TestCase):
    def test_same_port_allocation(self):
        if "TRAVIS" in os.environ:
            self.skipTest("dual-stack servers often have port conflicts on travis")
        sockets = bind_sockets(0, "localhost")
        try:
            port = sockets[0].getsockname()[1]
            self.assertTrue(all(s.getsockname()[1] == port for s in sockets[1:]))
        finally:
            for sock in sockets:
                sock.close()

    @unittest.skipIf(
        not hasattr(socket, "SO_REUSEPORT"), "SO_REUSEPORT is not supported"
    )
    def test_reuse_port(self):
        sockets = []  # type: List[socket.socket]
        socket, port = bind_unused_port(reuse_port=True)
        try:
            sockets = bind_sockets(port, "127.0.0.1", reuse_port=True)
            self.assertTrue(all(s.getsockname()[1] == port for s in sockets))
        finally:
            socket.close()
            for sock in sockets:
                sock.close()


text:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace TimekeeperWPF
{
    /// <summary>
    /// Interaction logic for FilterSelectorView.xaml
    /// </summary>
    public partial class FilterSelectorView : UserControl
    {
        public FilterSelectorView()
        {
            InitializeComponent();
        }
    }
}