text:
@interface TPRVideoPlayerHandler ()

- (void)openURL:(NSURL*)urlString;
- (void)handlePlayerEvent:(TPRPlayerEvent*)event;
- (TPRPlayerEvent*)parseEvent:(NSString*)query;
- (NSString*)encodeUrlString:(NSString *)string;
- (NSTimer*)startTimer:(NSTimeInterval)timeout target:(SEL)selector;
- (void)cancelTimer:(NSTimer*)timer;
- (void)timeoutOccuredOn:(NSTimer*)timer;
- (void)sendEvent:(NSString*)eventName arg1:(NSObject*)arg1 arg2:(NSObject*)arg2 arg3:(NSObject*)arg3;
- (void)sendEvent:(TPRPlayerEvent*)event;
- (NSString*)getAdvertisingID;
- (void)updateVolume;
- (void)initLocationServices;
- (CLLocation*)getLastKnownLocation;
- (void)updateLocationToPlayer;
- (void)startAdTrackingAnalytics;
- (void)createAdTrackers;
- (void)startAdTracking;
- (void)stopAdTracking;

@property (strong) TRDPMoatWebTracker *moatWebTracker;
@property (strong, readonly) CLLocationManager* locationManager;
@property (strong) TPRPlacementType* placementType;
@property (strong) AVAudioSession* avSession;
@end

@implementation TPRVideoPlayerHandler

NSString *const PLAYER_URL_BASE = @"%@//d1c13tt6n7tja5.cloudfront.net/tags/%@/sdk/LATEST/sdk_player.v3.html?env=%@&cid=%@&playerid=%@&adsdk=%@&type=%@&customization=%@&adverification=%@";

NSString *const TPR_PLAYER_NOTIFICATION = @"TPRPlayerNotification";
NSString *const PLAYER_EVENT_CUSTOM_SCHEME = @"thirdpresence";
NSString *const PLAYER_EVENT_HOST_NAME = @"onPlayerEvent";

NSInteger const LOCATION_DISTANCE_FILTER = 1000;
NSTimeInterval const LOCATION_EXPIRATION_LIMIT = 3600;

NSString *const AD_VERIFICATION_TYPE_MOAT = @"moat";

static void *VolumeObserverContext = &VolumeObserverContext;

- (instancetype)initWithPlayer:(TPRWebView *)webView
                   environment:(NSDictionary *)environment
                        params:(NSDictionary *)playerParams
                 placementType:(TPRPlacementType*)placementType{
    self = [super init];

    _webView = webView;
    _environment = environment;
    _playerParams = playerParams;
    _placementType = placementType;

    _playerTimeout = TPR_PLAYER_DEFAULT_TIMEOUT;
    _loadAdTimeout = TPR_PLAYER_DEFAULT_TIMEOUT;

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(appWillResignActive:)
                                                 name:UIApplicationWillResignActiveNotification
                                               object:nil];

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(appDidBecomeActive:)
                                                 name:UIApplicationDidBecomeActiveNotification
                                               object:nil];

    _avSession = [AVAudioSession sharedInstance];
    [_avSession addObserver:self
                 forKeyPath:@"outputVolume"
                    options:NSKeyValueObservingOptionNew
                    context:VolumeObserverContext];

    _webView.delegate = self;

    [self startAdTrackingAnalytics];
    [self initLocationServices];

    _adPlaying = NO;

    return self;
}

- (void)dealloc {
    [self resetState];
    [self removePlayer];
}

- (void)appWillResignActive:(NSNotification*)note {
    if (_adPlaying) {
        _adPlayPending = YES;
    }
}

- (void)appDidBecomeActive:(NSNotification*)note {
    if (_adPlayPending) {
        _adPlayPending = NO;
        [_webView startAd];
    }
}

- (void)loadPlayer {
    [self resetState];

    NSString* env = [self.environment objectForKey:TPR_ENVIRONMENT_KEY_SERVER];
    if (!env) {
        env = TPR_SERVER_TYPE_PRODUCTION;
    }

    NSString* account = [self.environment objectForKey:TPR_ENVIRONMENT_KEY_ACCOUNT];
    if (!account) {
        NSError* error = [NSError errorWithDomain:TPR_AD_SDK_ERROR_DOMAIN
                                             code:TPR_ERROR_PLAYER_INIT_FAILED
                                         userInfo:[NSDictionary dictionaryWithObject:@"Environment dictionary does not contain account id" forKey:NSLocalizedDescriptionKey]];
        [self sendEvent:TPR_EVENT_NAME_PLAYER_ERROR arg1:error arg2:nil arg3:nil];
        return;
    }

    NSString* placementId = [self.environment objectForKey:TPR_ENVIRONMENT_KEY_PLACEMENT_ID];
    if (!placementId) {
        NSError* error = [NSError errorWithDomain:TPR_AD_SDK_ERROR_DOMAIN
                                             code:TPR_ERROR_PLAYER_INIT_FAILED
                                         userInfo:[NSDictionary dictionaryWithObject:@"Environment dictionary does not contain placementid id" forKey:NSLocalizedDescriptionKey]];
        [self sendEvent:TPR_EVENT_NAME_PLAYER_ERROR arg1:error arg2:nil arg3:nil];
        return;
    }

    NSString* versionString = [self.environment objectForKey:TPR_ENVIRONMENT_KEY_SDK_VERSION];

    if (!versionString) {
        NSBundle* libBundle = [NSBundle bundleWithIdentifier:@"com.thirdpresence.ThirdpresenceAdSDK"];
        if (!libBundle) {
            libBundle = [NSBundle bundleWithIdentifier:@"org.cocoapods.thirdpresence-ad-sdk-ios"];
        }

        versionString = [libBundle objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
    }

    if (!versionString) {
        NSDictionary *dictionary = [[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"ThirdpresenceAdSDK-Info" ofType:@"plist"]];

        versionString = [dictionary objectForKey:@"CFBundleShortVersionString"];
    }

    NSString *extSdkName = [self.environment objectForKey:TPR_ENVIRONMENT_KEY_EXT_SDK];
    if (extSdkName) {
        NSString *extSdkVer = [self.environment objectForKey:TPR_ENVIRONMENT_KEY_EXT_SDK_VERSION];
        if (!extSdkVer) {
            extSdkVer = @"";
        }
        versionString = [NSString stringWithFormat:@"%@,%@,%@", versionString, extSdkName, extSdkVer];
    }

    NSString *customization = nil;
    NSMutableDictionary *params;

    if (_playerParams) {
        params = [NSMutableDictionary dictionaryWithDictionary:_playerParams];
    } else {
        params = [NSMutableDictionary dictionaryWithCapacity:5];
    }

    if (![params objectForKey:TPR_PLAYER_PARAMETER_KEY_DEVICE_ID]) {
        NSString* advertisingIdentifier = [self getAdvertisingID];
        if (advertisingIdentifier) {
            [params setValue:advertisingIdentifier forKey:TPR_PLAYER_PARAMETER_KEY_DEVICE_ID];
        }
    }

    if (![params objectForKey:TPR_PLAYER_PARAMETER_KEY_BUNDLE_ID]) {
        NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];
        if (bundleIdentifier) {
             [params setValue:bundleIdentifier forKey:TPR_PLAYER_PARAMETER_KEY_BUNDLE_ID];
        }
    }

    NSBundle *appBundle = [NSBundle mainBundle];
    NSString *appName = [appBundle objectForInfoDictionaryKey:@"CFBundleDisplayName"];
    if (!appName) {
        appName = [appBundle objectForInfoDictionaryKey:@"CFBundleName"];
    }

    if (appName) {
        if (![params objectForKey:TPR_PLAYER_PARAMETER_KEY_APP_NAME]) {
            [params setValue:appName forKey:TPR_PLAYER_PARAMETER_KEY_APP_NAME];
        }

        if (![params objectForKey:TPR_PLAYER_PARAMETER_KEY_APP_VERSION]) {
            NSString *version = [appBundle objectForInfoDictionaryKey:@"CFBundleVersion"];
            if (version) {
                [params setValue:version forKey:TPR_PLAYER_PARAMETER_KEY_APP_VERSION];
            }
        }

        if (![params objectForKey:TPR_PLAYER_PARAMETER_KEY_PUBLISHER]) {
            [params setValue:appName forKey:TPR_PLAYER_PARAMETER_KEY_PUBLISHER];
        }
    }

    CLLocation* location = [self getLastKnownLocation];
    if (location) {
        CLLocationDegrees latitude = location.coordinate.latitude;
        CLLocationDegrees longitude = location.coordinate.longitude;
        TPRLog(@"[TPR] Location available %f,%f", latitude, longitude);

        [params setValue:[NSString stringWithFormat:@"%f", latitude] forKey:TPR_PLAYER_PARAMETER_KEY_GEO_LAT];
        [params setValue:[NSString stringWithFormat:@"%f", longitude] forKey:TPR_PLAYER_PARAMETER_KEY_GEO_LON];
    } else {
        TPRLog(@"[TPR] Location data not available");
    }

    NSError *error;
    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:params
                                                       options:0
                                                         error:&error];
    if (! jsonData) {
        NSError* error = [NSError errorWithDomain:TPR_AD_SDK_ERROR_DOMAIN
                                             code:TPR_ERROR_PLAYER_INIT_FAILED
                                         userInfo:[NSDictionary dictionaryWithObject:@"Invalid player parameters"
                                                                              forKey:NSLocalizedDescriptionKey]];
        [self sendEvent:TPR_EVENT_NAME_PLAYER_ERROR arg1:error arg2:nil arg3:nil];
        return;
    }

    customization = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];

    NSString *useHTTP = [self.environment objectForKey:TPR_ENVIRONMENT_KEY_USE_INSECURE_HTTP];
    NSString *protocol = [useHTTP isEqualToString:TPR_VALUE_TRUE] ? @"http:" : @"https:";

    NSString* enableMoat = [self.environment objectForKey:TPR_ENVIRONMENT_KEY_MOAT_AD_TRACKING];

    _playerPageURL = [NSString stringWithFormat:PLAYER_URL_BASE,
                      protocol,
                      env,
                      env,
                      account,
                      placementId,
                      versionString,
                      _placementType,
                      customization ? [self encodeUrlString:customization] : @"",
                      [enableMoat isEqualToString:TPR_VALUE_FALSE] ? @"" : AD_VERIFICATION_TYPE_MOAT
                     ];

    _playerTimeoutTimer = [self startTimer:_playerTimeout target:@selector(timeoutOccuredOn:)];

    TPRLog(@"Loading player from URL: %@", _playerPageURL);

    [_webView prepare:_playerPageURL];

}

- (void)loadAd {
    if (_adDisplayed) {
        NSError* error = [NSError errorWithDomain:TPR_AD_SDK_ERROR_DOMAIN
                                             code:TPR_ERROR_INVALID_STATE
                                         userInfo:[NSDictionary dictionaryWithObject:@"An ad is being displayed. The reset message needs to be passed before loading a new ad" forKey:NSLocalizedDescriptionKey]];
        [self sendEvent:TPR_EVENT_NAME_PLAYER_ERROR arg1:error arg2:nil arg3:nil];
    } else if (_adLoaded) {
        NSError* error = [NSError errorWithDomain:TPR_AD_SDK_ERROR_DOMAIN
                                             code:TPR_ERROR_INVALID_STATE
                                         userInfo:[NSDictionary dictionaryWithObject:@"An ad is already loaded. The reset message needs to be passed before loading a new ad" forKey:NSLocalizedDescriptionKey]];
        [self sendEvent:TPR_EVENT_NAME_PLAYER_ERROR arg1:error arg2:nil arg3:nil];
    } else if (_playerLoaded) {
        _adLoadPending = NO;
        if (!_adLoading) {
            _adLoading = YES;
            [self createAdTrackers];
            _loadAdTimeoutTimer = [self startTimer:_loadAdTimeout target:@selector(timeoutOccuredOn:)];
            [_webView loadAd];
        }
    } else {
        _adLoadPending = YES;
    }
}

- (void)displayAd {
    if (!_adLoaded) {
        NSError* error = [NSError errorWithDomain:TPR_AD_SDK_ERROR_DOMAIN
                                             code:TPR_ERROR_INVALID_STATE
                                         userInfo:[NSDictionary dictionaryWithObject:@"Ad not loaded yet" forKey:NSLocalizedDescriptionKey]];
        [self sendEvent:TPR_EVENT_NAME_PLAYER_ERROR arg1:error arg2:nil arg3:nil];

    }
    else if (_adDisplayed) {
        NSError* error = [NSError errorWithDomain:TPR_AD_SDK_ERROR_DOMAIN
                                             code:TPR_ERROR_INVALID_STATE
                                         userInfo:[NSDictionary dictionaryWithObject:@"Ad already shown" forKey:NSLocalizedDescriptionKey]];
        [self sendEvent:TPR_EVENT_NAME_PLAYER_ERROR arg1:error arg2:nil arg3:nil];
    }
    else {
        [self startAdTracking];
        [self updateVolume];
        [_webView startAd];
        _adPlaying = YES;
    }
}

- (void)resetState {
    [self stopAdTracking];

    [_webView reset];

    [self cancelTimer:_playerTimeoutTimer];
    _playerTimeoutTimer = nil;
    [self cancelTimer:_loadAdTimeoutTimer];
    _loadAdTimeoutTimer = nil;

    _adLoadPending = NO;
    _adLoaded = NO;
    _adLoading = NO;
    _adDisplayed = NO;
    _adPlaying = NO;
}

- (void)removePlayer {
    _playerLoading = NO;
    _playerLoaded = NO;

    [[NSNotificationCenter defaultCenter] removeObserver:self];

    _webView.delegate = nil;
    _webView = nil;

    [_avSession removeObserver:self
                    forKeyPath:@"outputVolume"
                       context:VolumeObserverContext];
    _avSession = nil;

    [_locationManager  stopMonitoringSignificantLocationChanges];
    _locationManager.delegate = nil;
    _locationManager = nil;

    _playerParams = nil;
    _environment = nil;
    _playerPageURL = nil;
    _placementType = nil;
}

#pragma mark - UIWebViewDelegate

- (void)webViewDidStartLoad:(UIWebView *)webView {
}

- (void)webViewDidFinishLoad:(UIWebView *)webView {
}

- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
    if (!_playerLoaded) {
        _playerLoading = NO;
        NSError* error = [NSError errorWithDomain:TPR_AD_SDK_ERROR_DOMAIN
                                             code:TPR_ERROR_NETWORK_FAILURE
                                         userInfo:[NSDictionary dictionaryWithObject:@"Network failure while loading the player" forKey:NSLocalizedDescriptionKey]];
        [self resetState];
        [self sendEvent:TPR_EVENT_NAME_PLAYER_ERROR arg1:error arg2:nil arg3:nil];
    } else if (!_adLoaded) {
        NSError* error = [NSError errorWithDomain:TPR_AD_SDK_ERROR_DOMAIN
                                             code:TPR_ERROR_NETWORK_FAILURE
                                         userInfo:[NSDictionary dictionaryWithObject:@"Network failure while loading an ad" forKey:NSLocalizedDescriptionKey]];
        [self resetState];
        [self sendEvent:TPR_EVENT_NAME_PLAYER_ERROR arg1:error arg2:nil arg3:nil];
    }
}

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
    NSString* scheme = request.URL.scheme;

    if ([scheme isEqualToString:PLAYER_EVENT_CUSTOM_SCHEME] && [request.URL.host isEqualToString:PLAYER_EVENT_HOST_NAME]) {
        [self handlePlayerEvent:[self parseEvent:request.URL.query]];
    } else if ((navigationType != UIWebViewNavigationTypeOther && navigationType != UIWebViewNavigationTypeLinkClicked) || !_adLoaded) {
        return YES;
    }
    else {
        BOOL click = ![_playerPageURL isEqualToString:[request.mainDocumentURL absoluteString]];
        if (click) {
            if ([scheme hasPrefix:@"http"]) {
                [self openURL:request.URL];
            }
        }
    }
    return NO;
}

#pragma mark - CLLocationManagerDelegate

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
    CLLocation* location = [locations lastObject];
    NSDate* eventDate = location.timestamp;
    NSTimeInterval howRecent = [eventDate timeIntervalSinceNow];
    if (howRecent < LOCATION_EXPIRATION_LIMIT) {
        TPRLog(@"Stop updating location");
        [_locationManager stopUpdatingLocation];
    }

    if (_locationTimeStamp == nil || [_locationTimeStamp compare:eventDate] == NSOrderedAscending) {
        TPRLog(@"[TPR] Location updated %f,%f", location.coordinate.latitude, location.coordinate.longitude);
        if (_playerLoading) {
            _playerLocationUpdatePending = YES;
        }
        else if (_playerLoaded) {
            [self updateLocationToPlayer];
        }
    }
}

#pragma mark - PrivateMethods

- (void)openURL:(NSURL*)url {
    if ([[UIApplication sharedApplication] canOpenURL:url]) {
        [self sendEvent:TPR_EVENT_NAME_AD_LEFT_APPLICATION arg1:url arg2:nil arg3:nil];
        [[UIApplication sharedApplication] openURL:url];
    }
}

- (void)handlePlayerEvent:(TPRPlayerEvent*)event {
    NSString* eventName = [event objectForKey:TPR_EVENT_KEY_NAME];
    if ([eventName isEqualToString:TPR_EVENT_NAME_PLAYER_READY]) {
        _playerLoading = NO;
        _playerLoaded = YES;
        [self cancelTimer:_playerTimeoutTimer];
        _playerTimeoutTimer = nil;
        if (_playerLocationUpdatePending) {
            [self updateLocationToPlayer];
        }
        if (_adLoadPending) {
            [self loadAd];
        }
    } else if ([eventName isEqualToString:TPR_EVENT_NAME_AD_LOADED]) {
        [self cancelTimer:_loadAdTimeoutTimer];
        _loadAdTimeoutTimer = nil;
        _adLoadPending = NO;
        _adLoading = NO;
        _adLoaded = YES;
    } else if ([eventName isEqualToString:TPR_EVENT_NAME_AD_STOPPED] ||
               [eventName isEqualToString:TPR_EVENT_NAME_AD_VIDEO_COMPLETE] ||
               [eventName isEqualToString:TPR_EVENT_NAME_AD_SKIPPED]) {
        _adPlaying = NO;

    }

    [self sendEvent:event];
}

- (TPRPlayerEvent*)parseEvent:(NSString*)query {
    NSMutableDictionary* event = [NSMutableDictionary dictionaryWithCapacity:10];
    NSArray *queryItems = [query componentsSeparatedByString:@"&"];

    for (NSString *item in queryItems) {
        NSArray *kvp = [item componentsSeparatedByString:@"="];
        if (kvp[0] && kvp[1]) {
            NSString *value = [[kvp[1]
                                stringByReplacingOccurrencesOfString:@"+" withString:@" "]
                               stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
            [event setObject:value forKey:kvp[0]];
        }
    }
    return event;
}

-(NSString *)encodeUrlString:(NSString *)string {
    return (__bridge_transfer NSString *) CFURLCreateStringByAddingPercentEscapes(NULL, (__bridge CFStringRef) string,
                                                                                NULL, (CFStringRef) @"!*'();:@&=+$,/?%#[]", kCFStringEncodingUTF8);
}

- (NSTimer*)startTimer:(NSTimeInterval) timeout target:(SEL)selector {
    NSTimer *timer = [NSTimer timerWithTimeInterval:timeout
                                             target:self
                                           selector:selector
                                           userInfo:nil
                                            repeats:NO];
    [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
    return timer;
}

- (void)cancelTimer:(NSTimer*)timer {
    if ([timer isValid]) {
        [timer invalidate];
    }
}

- (void)timeoutOccuredOn:(NSTimer*)timer {
    NSError* error;

    if (timer == _playerTimeoutTimer) {
        error = [NSError errorWithDomain:TPR_AD_SDK_ERROR_DOMAIN
                                    code:TPR_ERROR_NETWORK_TIMEOUT
                                userInfo:[NSDictionary dictionaryWithObject:@"Timeout occured while loading the player" forKey:NSLocalizedDescriptionKey]];
    } else if (timer == _loadAdTimeoutTimer) {
        error = [NSError errorWithDomain:TPR_AD_SDK_ERROR_DOMAIN
                                    code:TPR_ERROR_NETWORK_TIMEOUT
                                userInfo:[NSDictionary dictionaryWithObject:@"Timeout occured while loading an ad" forKey:NSLocalizedDescriptionKey]];
    }

    [self resetState];

    if (error) {
        [self sendEvent:TPR_EVENT_NAME_PLAYER_ERROR arg1:error arg2:nil arg3:nil];
    }
}

- (NSString*)getAdvertisingID {
    if ([ASIdentifierManager class]) {
        return [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
    }
    return nil;
}

- (void)updateVolume {
    if (_avSession) {
        float volume = [_avSession outputVolume];
        [_webView updateVolume:volume];
    }
}

- (void)initLocationServices {
    if ([CLLocationManager class] && [CLLocationManager authorizationStatus] >= kCLAuthorizationStatusAuthorizedAlways) {
        _locationManager = [[CLLocationManager alloc] init];
        _locationManager.delegate = (TPRVideoPlayerHandler<CLLocationManagerDelegate>*)self;
        _locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers;
        _locationManager.distanceFilter = LOCATION_DISTANCE_FILTER;
        CLLocation* location = [self getLastKnownLocation];
        NSDate *expiration = [NSDate dateWithTimeIntervalSinceNow:-LOCATION_EXPIRATION_LIMIT];
        if (location == nil || [location.timestamp compare:expiration] == NSOrderedAscending) {
            // Start location updates if most recent update older than the expiration limit
            TPRLog(@"Start to updating location");
            [_locationManager startUpdatingLocation];
        }
    }
}

- (CLLocation*)getLastKnownLocation {
    if ([CLLocationManager class]) {
        CLLocation* lastKnownLocation = nil;
        if ([CLLocationManager locationServicesEnabled] && [CLLocationManager authorizationStatus] >= kCLAuthorizationStatusAuthorizedAlways) {
            lastKnownLocation = [_locationManager location];
            _locationTimeStamp = lastKnownLocation.timestamp;
        }
        return lastKnownLocation;
    }
    return nil;
}

- (void)updateLocationToPlayer {
    if ([CLLocationManager class]) {
        CLLocation* location = [self getLastKnownLocation];
        if (location) {
            [_webView updateLocationWithLatitude:location.coordinate.latitude longitude:location.coordinate.longitude];
        }
    }
}

- (void)startAdTrackingAnalytics {
    NSString* enableMoat = [self.environment objectForKey:TPR_ENVIRONMENT_KEY_MOAT_AD_TRACKING];
    if (![enableMoat isEqualToString:TPR_VALUE_FALSE]) {
        TRDPMoatOptions *options = [[TRDPMoatOptions alloc] init];
#ifdef DEBUG
        options.debugLoggingEnabled = true;
#endif
        [[TRDPMoatAnalytics sharedInstance] startWithOptions:options];
    }
}

- (void)createAdTrackers {
    NSString* enableMoat = [self.environment objectForKey:TPR_ENVIRONMENT_KEY_MOAT_AD_TRACKING];
    if (!enableMoat || [enableMoat isEqualToString:TPR_VALUE_TRUE]) {
        self.moatWebTracker = [TRDPMoatWebTracker trackerWithWebComponent:_webView];
        TPRLog(@"[TPR] MOAT ad tracker enabled");
    } else {
        TPRLog(@"[TPR] MOAT SDK not available");
    }
}

- (void)startAdTracking {
    if (self.moatWebTracker) {
        [self.moatWebTracker startTracking];
    }
}

- (void)stopAdTracking {
    if (self.moatWebTracker) {
        [self.moatWebTracker stopTracking];
        self.moatWebTracker = nil;
    }
}

- (void)sendEvent:(NSString*)eventName arg1:(NSObject*)arg1 arg2:(NSObject*)arg2 arg3:(NSObject*)arg3  {
    TPRPlayerEvent *event = [NSMutableDictionary dictionaryWithCapacity:4];
    [event setValue:eventName forKey:TPR_EVENT_KEY_NAME];
    if (arg1) [event setValue:arg1 forKey:TPR_EVENT_KEY_ARG1];
    if (arg2) [event setValue:arg2 forKey:TPR_EVENT_KEY_ARG2];
    if (arg3) [event setValue:arg3 forKey:TPR_EVENT_KEY_ARG3];
    [self sendEvent:event];
}

- (void)sendEvent:(TPRPlayerEvent*)event {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
        [[NSNotificationCenter defaultCenter] postNotificationName:TPR_PLAYER_NOTIFICATION
                                                            object:self
                                                          userInfo:event];
    });
}

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context {

    if (context == VolumeObserverContext) {
        [self updateVolume];
    } else {
        [super observeValueForKeyPath:keyPath
                             ofObject:object
                               change:change
                              context:context];
    }
}

@end


text:
<?php

final class AlmanacServiceNameTransaction
  extends AlmanacServiceTransactionType {

  const TRANSACTIONTYPE = 'almanac:service:name';

  public function generateOldValue($object) {
    return $object->getName();
  }

  public function applyInternalEffects($object, $value) {
    $object->setName($value);
  }

  public function getTitle() {
    return pht(
      '%s renamed this service from %s to %s.',
      $this->renderAuthor(),
      $this->renderOldValue(),
      $this->renderNewValue());
  }

  public function getTitleForFeed() {
    return pht(
      '%s renamed %s from %s to %s.',
      $this->renderAuthor(),
      $this->renderObject(),
      $this->renderOldValue(),
      $this->renderNewValue());
  }

  public function validateTransactions($object, array $xactions) {
    $errors = array();

    if ($this->isEmptyTextTransaction($object->getName(), $xactions)) {
      $errors[] = $this->newRequiredError(
        pht('Almanac services must have a name.'));
    }

    foreach ($xactions as $xaction) {
      $name = $xaction->getNewValue();

      $message = null;
      try {
        AlmanacNames::validateName($name);
      } catch (Exception $ex) {
        $message = $ex->getMessage();
      }

      if ($message !== null) {
        $errors[] = $this->newInvalidError($message, $xaction);
        continue;
      }

      if ($name === $object->getName()) {
        continue;
      }

      $other = id(new AlmanacServiceQuery())
        ->setViewer(PhabricatorUser::getOmnipotentUser())
        ->withNames(array($name))
        ->executeOne();
      if ($other && ($other->getID() != $object->getID())) {
        $errors[] = $this->newInvalidError(
          pht('Almanac services must have unique names.'),
          $xaction);
        continue;
      }

      $namespace = AlmanacNamespace::loadRestrictedNamespace(
        $this->getActor(),
        $name);
      if ($namespace) {
        $errors[] = $this->newInvalidError(
          pht(
            'You do not have permission to create Almanac services '.
            'within the "%s" namespace.',
            $namespace->getName()),
          $xaction);
        continue;
      }
    }

    return $errors;
  }
}

redpajama_set_name: RedPajamaGithub


text:
package tamaized.voidcraft.common.machina.tileentity;

import net.minecraft.entity.EntityLivingBase;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.ItemStackHandler;
import tamaized.tammodized.common.particles.network.ParticleFluffPacketHandler;
import tamaized.tammodized.common.tileentity.TamTileEntityInventory;
import tamaized.voidcraft.VoidCraft;
import tamaized.voidcraft.common.voidicpower.TileEntityVoidicPowerInventory;
import tamaized.voidcraft.registry.VoidCraftBlocks;

import javax.annotation.Nullable;
import java.util.List;

public class TileEntityRealityTeleporter extends TileEntityVoidicPowerInventory {

	public TamTileEntityInventory.ItemStackFilterHandler SLOT_INPUT;

	private BlockPos link;
	private boolean isOn = false;

	public TileEntityRealityTeleporter() {
		super();
	}

	@Override
	protected ItemStackHandler[] register() {
		return new ItemStackHandler[]{SLOT_INPUT = new TamTileEntityInventory.ItemStackFilterHandler(new ItemStack[]{new ItemStack(VoidCraftBlocks.realityHole)}, true, new ItemStack[0], true)};
	}

	@Nullable
	@Override
	protected IItemHandler getCap(EnumFacing face) {
		return SLOT_INPUT;
	}

	@Override
	public int getMaxPower() {
		return 50000;
	}

	@Override
	public int maxPowerTransfer() {
		return 320;
	}

	@Override
	public boolean canOutputPower(EnumFacing face) {
		return false;
	}

	@Override
	public boolean canInputPower(EnumFacing face) {
		return face != EnumFacing.UP;
	}

	public int getPowerUse() {
		return 2000;
	}

	public boolean canUse() {
		return getPowerAmount() >= getPowerUse() && !SLOT_INPUT.getStackInSlot(0).isEmpty() && SLOT_INPUT.getStackInSlot(0).getItem() == Item.getItemFromBlock(VoidCraft.blocks.realityHole);
	}

	public void useResources() {
		drainPower(getPowerUse());
		SLOT_INPUT.getStackInSlot(0).shrink(1);
		for (float xf = 0.0F; xf <= 1.0F; xf += 0.25F)
			for (float zf = 0.0F; zf <= 1.0F; zf += 0.25F)
				for (float yf = 0.0F; yf <= 1.0F; yf += 0.25F) {
					if (world.rand.nextInt(4) != 0)
						continue;
					ParticleFluffPacketHandler.spawnOnServer(world, new Vec3d(pos.getX() + xf, pos.getY() + yf + 1.0F, pos.getZ() + zf), new Vec3d(0, 0, 0), world.rand.nextInt(20 * 2) + 20, -((world.rand.nextFloat() * 0.02F) + 0.01F), (world.rand.nextFloat() * 0.50F) + 0.25F, 0xFFFFFFFF);
				}
	}

	public void useLinkResources() {
		if (link == null)
			return;
		TileEntity te = world.getTileEntity(link);
		if (te != null && te instanceof TileEntityRealityTeleporter) {
			TileEntityRealityTeleporter teLink = (TileEntityRealityTeleporter) te;
			teLink.useResources();
		}
	}

	@Override
	protected void readNBT(NBTTagCompound nbt) {
		int[] loc = nbt.getIntArray("link");
		link = loc.length == 3 ? new BlockPos(loc[0], loc[1], loc[2]) : null;
		isOn = nbt.getBoolean("isOn");
	}

	@Override
	protected NBTTagCompound writeNBT(NBTTagCompound nbt) {
		nbt.setBoolean("isOn", isOn);
		nbt.setIntArray("link", link != null ? new int[]{link.getX(), link.getY(), link.getZ()} : new int[]{});
		return nbt;
	}

	@Override
	protected void onUpdate() {
		boolean flag = world.isBlockIndirectlyGettingPowered(getPos()) > 0;
		validateLink();
		if (link != null) {
			if (!isOn && flag) {
				List<EntityLivingBase> list = world.getEntitiesWithinAABB(EntityLivingBase.class, new AxisAlignedBB(getPos().up()));
				if (!list.isEmpty() && canUse() && canTeleportToLink()) {
					useResources();
					useLinkResources();
					for (EntityLivingBase e : list) {
						e.setPositionAndUpdate(link.getX() + 0.5, link.getY() + 1, link.getZ() + 0.5);
					}
				}
			}
		}
		isOn = flag;
	}

	public boolean setLink(BlockPos pos) {
		link = pos;
		validateLink();
		return link != null;
	}

	public BlockPos getLink() {
		return link;
	}

	private void validateLink() {
		if (link == null)
			return;
		TileEntity te = world.getTileEntity(link);
		if (!(te instanceof TileEntityRealityTeleporter))
			link = null;
	}

	private boolean canTeleportToLink() {
		if (link == null)
			return false;
		TileEntity te = world.getTileEntity(link);
		if (te != null && te instanceof TileEntityRealityTeleporter) {
			TileEntityRealityTeleporter teLink = (TileEntityRealityTeleporter) te;
			return teLink.canUse();
		}
		return false;
	}

}


text:
import sys

if sys.version_info <= (2,7):
  # SystemExit defaults to returning 1 when printing a string to stderr
  raise SystemExit("You are using python %s, but version 2.7 or greater is "
                   "required" % sys.version_info)

required = 0
optional = 0

# Test for whisper
try:
  import whisper
except ImportError:
  # No? test for ceres
  try:
    import ceres
    # We imported ceres, but not whisper so it's an optional dependency
    sys.stderr.write("[OPTIONAL] Unable to import the 'whisper' module. Without it the webapp will be unable to read .wsp files\n")
    optional += 1
  except ImportError:
    sys.stderr.write("[REQUIRED] Unable to import the 'whisper' or 'ceres' modules, please download this package from the Graphite project page and install it.\n")
    required += 1

# Test for cairocffi or pycairo
try:
  import cairocffi as cairo
except ImportError:
  sys.stderr.write("[REQUIRED] Unable to import the 'cairocffi' module, attempting to fall back to pycairo\n")
  try:
    import cairo
  except ImportError:
    sys.stderr.write("[REQUIRED] Unable to import the 'cairo' module, do you have pycairo installed for python %s?\n" % sys.version_info.major)
    cairo = None
    required += 1

# Test that pycairo has the PNG backend
try:
  if cairo:
    surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 10, 10)
    del surface
except Exception:
  sys.stderr.write("[REQUIRED] Failed to create an ImageSurface with cairo, you probably need to recompile cairo with PNG support\n")
  required += 1

# Test that cairo can find fonts
try:
  if cairo:
    surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 10, 10)
    context = cairo.Context(surface)
    context.font_extents()
    del surface, context
except Exception:
  sys.stderr.write("[REQUIRED] Failed to create text with cairo, this probably means cairo cant find any fonts. Install some system fonts and try again\n")

# Test for django
try:
  import django
except ImportError:
  sys.stderr.write("[REQUIRED] Unable to import the 'django' module, do you have Django installed for python %s?\n" % sys.version_info.major)
  django = None
  required += 1

# Test for pytz
try:
  import pytz
except ImportError:
  sys.stderr.write("[REQUIRED] Unable to import the 'pytz' module, do you have pytz module installed for python %s?\n" % sys.version_info.major)
  required += 1

# Test for pyparsing
try:
  import pyparsing
except ImportError:
  sys.stderr.write("[REQUIRED] Unable to import the 'pyparsing' module, do you have pyparsing module installed for python %s?\n" % sys.version_info.major)
  required += 1

# Test for django-tagging
try:
  import tagging
except ImportError:
  sys.stderr.write("[REQUIRED] Unable to import the 'tagging' module, do you have django-tagging installed for python %s?\n" % sys.version_info.major)
  required += 1

if django and django.VERSION[:2] < (1,8):
  sys.stderr.write("[REQUIRED] You have django version %s installed, but version 1.8 or greater is required\n" % django.get_version())
  required += 1

# Test for python-memcached
try:
  import memcache
except ImportError:
  sys.stderr.write("[OPTIONAL] Unable to import the 'memcache' module, do you have python-memcached installed for python %s? This feature is not required but greatly improves performance.\n" % sys.version_info.major)
  optional += 1

# Test for python-ldap
try:
  import ldap
except ImportError:
  sys.stderr.write("[OPTIONAL] Unable to import the 'ldap' module, do you have python-ldap installed for python %s? Without python-ldap, you will not be able to use LDAP authentication in the graphite webapp.\n" % sys.version_info.major)
  optional += 1

# Test for txamqp
try:
  import txamqp
except ImportError:
  sys.stderr.write("[OPTIONAL] Unable to import the 'txamqp' module, this is required if you want to use AMQP as an input to Carbon. Note that txamqp requires python 2.5 or greater.\n")
  optional += 1

# Test for python-rrdtool
try:
  import rrdtool
except ImportError:
  sys.stderr.write("[OPTIONAL] Unable to import the 'python-rrdtool' module, this is required for reading RRD.\n")
  optional += 1

# Test for whitenoise
try:
    import whitenoise
except ImportError:
    sys.stderr.write("[OPTIONAL] Unable to import the 'whitenoise' module. This is useful for serving static files.\n")
    optional += 1

# Test for pyhash
try:
    import pyhash
except ImportError:
    sys.stderr.write("[OPTIONAL] Unable to import the 'pyhash' module. This is useful for fnv1_ch hashing support.\n")
    optional += 1

if optional:
  sys.stderr.write("%d optional dependencies not met. Please consider the optional items before proceeding.\n" % optional)
else:
  print("All optional dependencies are met.")

if required:
  sys.stderr.write("%d necessary dependencies not met. Graphite will not function until these dependencies are fulfilled.\n" % required)
  sys.exit(1)
else:
  print("All necessary dependencies are met.")


text:
package com.ruesga.phoenix.dialect;

import com.ruesga.phoenix.jpa.JpaEntityManager;
import com.ruesga.phoenix.jpa.entities.*;
import com.ruesga.phoenix.jpa.entities.Parameter;
import org.apache.commons.math3.util.Pair;
import org.hibernate.Criteria;
import org.junit.*;
import org.junit.rules.ExpectedException;
import org.junit.runners.MethodSorters;

import javax.persistence.*;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaUpdate;
import javax.persistence.criteria.Root;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.*;

@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class PhoenixDialectTest {

    @ClassRule
    public static HBaseClusterTestRule cluster = new HBaseClusterTestRule("hbase-site.xml");

    @Rule
    public ExpectedException thrown = ExpectedException.none();

    private static EntityManager em;
    private static SimpleDateFormat DATE_FORMATTER = new SimpleDateFormat("yyyy-MM-dd");

    @BeforeClass
    public static void setUpBeforeClass() throws Exception {
        Properties dbProps = new Properties();
        try {
            dbProps.load(PhoenixDialectTest.class.getResourceAsStream("/database.properties"));
        } catch (Exception e) {
        }
        if (!dbProps.containsKey("test.phoenix.dfs.nodenames") ||
                !dbProps.containsKey("test.phoenix.dfs.db.path")) {
            Assert.fail("Test are not configured. Add a file named 'database.properties' to src/test/resources " +
                "and add the following properties\n\n" +
                "    test.phoenix.dfs.nodenames: HBase zookeepers nodenames in the form <server>:<port>\n" +
                "    test.phoenix.dfs.db.path: HBase path in HDFS\n\n");
            return;
        }

        PhoenixDialect.register();
        em = JpaEntityManager.getInstance().createEntityManager();
    }

    @AfterClass
    public static void tearDownAfterClass() {
        if (em != null) {
            em.close();
        }
        JpaEntityManager.getInstance().close();
    }

    @Test
    public void test001_SelectAll() {
        TypedQuery<Employee> q = em.createQuery("select e from employee e", Employee.class);
        List<Employee> e = q.getResultList();
        Assert.assertEquals(31, e.size());
    }

    @Test
    public void test002_SelectRowKey() {
        TypedQuery<Employee> q = em.createQuery("select e from employee e where e.empNo = :empNo", Employee.class);
        q.setParameter("empNo", 10002);
        Employee e = q.getSingleResult();
        Assert.assertNotNull(e);
        Assert.assertEquals("Bezalel", e.getFirstName());
    }

    @Test
    public void test003_SelectLazyLoad() {
        TypedQuery<Employee> q = em.createQuery("select e from employee e where e.empNo = :empNo", Employee.class);
        q.setParameter("empNo", 10010);
        Employee e = q.getSingleResult();

        Set<DepartmentEmployee> departments = e.getDepartments();
        Assert.assertNotNull(departments);
        Assert.assertEquals(2, departments.size());
        Department department = findActiveEntity(departments).getDepartment();
        Assert.assertNotNull(department);
        Assert.assertEquals("Finance", department.getDeptName());
    }

    @Test
    public void test004_SelectJoin() {
        TypedQuery<Salary> q = em.createQuery("select s from salary s inner join s.employee as e " +
                "where e.empNo = :empNo", Salary.class);
        q.setParameter("empNo", 10001);
        List<Salary> salaries = q.getResultList();
        Assert.assertEquals(17, salaries.size());
        Salary salary = findActiveEntity(salaries);
        Assert.assertNotNull(salary);
        Assert.assertEquals(Double.valueOf(88958d), salary.getSalary());
    }

    @Test
    public void test005_SelectCount() {
        TypedQuery<Long> q = em.createQuery("select count(d.deptNo) from department d", Long.class);
        long count = q.getSingleResult();
        Assert.assertEquals(3L, count);
    }

    @Test
    public void test006_SelectOrderBy() {
        TypedQuery<Department> q = em.createQuery(
                "select d from department d order by d.deptName desc", Department.class);
        List<Department> departments = q.getResultList();
        Assert.assertEquals(3L, departments.size());
        Assert.assertEquals("Finance", departments.get(2).getDeptName());
    }

    @Test
    @SuppressWarnings("rawtypes")
    public void test007_SelectGroupBy() {
        TypedQuery<Pair> q = em.createQuery(
                "select new org.apache.commons.math3.util.Pair(s.employee.empNo, sum(s.salary)) from salary s " +
                "group by s.employee.empNo order by s.employee.empNo asc", Pair.class);
        List<Pair> salaries = q.getResultList();
        Assert.assertEquals(31L, salaries.size());
        Assert.assertEquals(Double.valueOf(1281612d), salaries.get(0).getSecond());
    }

    @Test
    @SuppressWarnings("rawtypes")
    public void test008_SelectHaving() {
        TypedQuery<Pair> q = em.createQuery(
                "select new org.apache.commons.math3.util.Pair(s.employee.empNo, sum(s.salary) as sal) from salary s " +
                "group by s.employee.empNo having sum(s.salary) > 1281612 order by sal desc", Pair.class);
        List<Pair> salaries = q.getResultList();
        Assert.assertEquals(4L, salaries.size());
        Assert.assertEquals(Double.valueOf(1604309d), salaries.get(0).getSecond());
    }

    @Test
    public void test009_SelectHint() {
        TypedQuery<Department> q = em.createQuery(" SELECT d from department d " +
                    "where d.deptName = :deptName", Department.class);
        ((org.hibernate.query.Query<Department>)q).addQueryHint(
                String.valueOf(new PhoenixDialect.SecondaryIndexHint(Department.class, "D_I0").build()));
        q.setParameter("deptName", "Finance");
        Department department = q.getSingleResult();
        Assert.assertEquals("Finance", department.getDeptName());
    }

    @Test
    public void test010_SelectLimit() {
        TypedQuery<Department> q = em.createQuery("select d from department d order by d.deptNo asc", Department.class);
        List<Department> departments = q.setMaxResults(2).getResultList();
        Assert.assertEquals(2L, departments.size());
        for (int i = 0; i < 2; i++) {
            Assert.assertEquals(i + 1, departments.get(i).getDeptNo());
        }
    }

    @Test
    public void test011_SelectFirstResultQuery() {
        TypedQuery<Employee> q = em.createQuery("select e from employee e order by e.empNo asc", Employee.class);
        List<Employee> employees = q.setFirstResult(5).getResultList();
        Assert.assertEquals(10006, employees.get(0).getEmpNo());
    }

    @Test
    public void test012_SelectPagedQuery() {
        TypedQuery<Employee> q = em.createQuery("select e from employee e order by e.empNo asc", Employee.class);
        List<Employee> employees = q.setMaxResults(5).setFirstResult(5).getResultList();
        Assert.assertEquals(5L, employees.size());
        Assert.assertEquals(10006, employees.get(0).getEmpNo());
    }

    @Test
    public void test100_FunctionPercentileCont() {
        TypedQuery<Double> q = em.createQuery("select percentile_cont_asc(0.90, s.salary) from salary s", Double.class);
        Double percentile = q.getSingleResult();
        Assert.assertEquals(Double.valueOf(89264d), percentile);

        q = em.createQuery("select percentile_cont_desc(0.90, s.salary) from salary s", Double.class);
        percentile = q.getSingleResult();
        Assert.assertEquals(Double.valueOf(44238d), percentile);
    }

    @Test
    public void test101_FunctionPercentileDisc() {
        TypedQuery<Double> q = em.createQuery("select percentile_disc_asc(0.90, s.salary) from salary s", Double.class);
        Double percentile = q.getSingleResult();
        Assert.assertEquals(Double.valueOf(89204d), percentile);

        q = em.createQuery("select percentile_disc_desc(0.90, s.salary) from salary s", Double.class);
        percentile = q.getSingleResult();
        Assert.assertEquals(Double.valueOf(44276d), percentile);
    }

    @Test
    public void test102_FunctionPercentRank() {
        TypedQuery<Double> q = em.createQuery("select percent_rank_asc(0.90, s.salary) from salary s", Double.class);
        Double percentile = q.getSingleResult();
        Assert.assertEquals(Double.valueOf(0d), percentile);

        q = em.createQuery("select percent_rank_desc(0.90, s.salary) from salary s", Double.class);
        percentile = q.getSingleResult();
        Assert.assertEquals(Double.valueOf(1d), percentile);
    }

    @Test
    public void test103_FunctionStdDevPop() {
        TypedQuery<Double> q = em.createQuery("select stddev_pop(s.salary) from salary s", Double.class);
        Double stddev = q.getSingleResult();
        Assert.assertTrue(Math.abs(stddev.doubleValue() - 16522.5459d) < 0.001d);
    }

    @Test
    public void test104_FunctionStdDevSamp() {
        TypedQuery<Double> q = em.createQuery("select stddev_samp(s.salary) from salary s", Double.class);
        Double stddev = q.getSingleResult();
        Assert.assertTrue(Math.abs(stddev.doubleValue() - 16546.8975d) < 0.001d);
    }

    @Test
    public void test105_FunctionUpper() {
        TypedQuery<String> q = em.createQuery("select upper('x') from dual d", String.class);
        String result = q.getSingleResult();
        Assert.assertEquals("X", result);
    }

    @Test
    public void test106_FunctionLower() {
        TypedQuery<String> q = em.createQuery("select lower('X') from dual d", String.class);
        String result = q.getSingleResult();
        Assert.assertEquals("x", result);
    }

    @Test
    public void test107_FunctionReverse() {
        TypedQuery<String> q = em.createQuery("select reverse('abc') from dual d", String.class);
        String result = q.getSingleResult();
        Assert.assertEquals("cba", result);
    }

    @Test
    public void test108_FunctionSubstr() {
        TypedQuery<String> q = em.createQuery("select substr('abcdef', 2, 3) from dual d", String.class);
        String result = q.getSingleResult();
        Assert.assertEquals("bcd", result);
    }

    @Test
    public void test109_FunctionInstr() {
        TypedQuery<Integer> q = em.createQuery("select instr('abcdef', 'bc') from dual d", Integer.class);
        Integer result = q.getSingleResult();
        Assert.assertEquals(Integer.valueOf(2), result);
    }

    @Test
    public void test110_FunctionTrim() {
        TypedQuery<String> q = em.createQuery("select trim(' abcdef ') from dual d", String.class);
        String result = q.getSingleResult();
        Assert.assertEquals("abcdef", result);
    }

    @Test
    public void test111_FunctionLTrim() {
        TypedQuery<String> q = em.createQuery("select ltrim(' abcdef ') from dual d", String.class);
        String result = q.getSingleResult();
        Assert.assertEquals("abcdef ", result);
    }

    @Test
    public void test112_FunctionRTrim() {
        TypedQuery<String> q = em.createQuery("select rtrim(' abcdef ') from dual d", String.class);
        String result = q.getSingleResult();
        Assert.assertEquals(" abcdef", result);
    }

    @Test
    public void test112_FunctionLPad() {
        TypedQuery<String> q = em.createQuery("select lpad('abcdef', 8) from dual d", String.class);
        String result = q.getSingleResult();
        Assert.assertEquals("  abcdef", result);
    }

    @Test
    public void test113_FunctionLength() {
        TypedQuery<Integer> q = em.createQuery("select length('abcdef') from dual d", Integer.class);
        Integer result = q.getSingleResult();
        Assert.assertEquals(Integer.valueOf("abcdef".length()), result);
    }

    @Test
    public void test114_FunctionRegexpSubstr() {
        TypedQuery<String> q = em.createQuery("select regexp_substr('na1-appsrv35-sj35', '[^-]+') from dual d", String.class);
        String result = q.getSingleResult();
        Assert.assertEquals("na1", result);
    }

    @Test
    public void test115_FunctionRegexpReplace() {
        TypedQuery<String> q = em.createQuery("select regexp_replace('abc123ABC', '[0-9]+', '#') from dual d", String.class);
        String result = q.getSingleResult();
        Assert.assertEquals("abc#ABC", result);
    }

    @Test
    public void test116_FunctionToCharDate() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        TypedQuery<String> q = em.createQuery("select to_char(current_date(), 'yyyy-MM-dd') from dual d", String.class);
        String result = q.getSingleResult();
        Assert.assertEquals(sdf.format(new Date()), result);
    }

    @Test
    public void test117_FunctionToCharNumber() {
        TypedQuery<String> q = em.createQuery("select to_char(23.5678, '#0.0') from dual d", String.class);
        String result = q.getSingleResult();
        Assert.assertEquals("23.6", result.replace(",", "."));
    }

    @Test
    public void test118_FunctionToDate() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        TypedQuery<Date> q = em.createQuery("select to_date('2017-02-23', 'yyyy-MM-dd', :tz) from dual d", Date.class);
        q.setParameter("tz", TimeZone.getDefault().getID());
        Date result = q.getSingleResult();
        Assert.assertEquals("2017-02-23", sdf.format(result));
    }

    @Test
    public void test119_FunctionToTime() {
        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
        sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
        TypedQuery<Date> q = em.createQuery("select to_time('2005-10-01 14:03:22.559') from dual d", Date.class);
        Date result = q.getSingleResult();
        Assert.assertEquals("14:03:22", sdf.format(result));
    }

    @Test
    public void test120_FunctionToTimestamp() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
        sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
        TypedQuery<Date> q = em.createQuery(
                "select to_timestamp('2005-10-01 14:03:22.559', 'yyyy-MM-dd HH:mm:ss.SSS') from dual d", Date.class);
        Date result = q.getSingleResult();
        Assert.assertEquals("2005-10-01 14:03:22.559", sdf.format(result));
    }

    @Test
    public void test121_FunctionConvertTz() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
        sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
        TypedQuery<Date> q = em.createQuery("select CONVERT_TZ(to_date('2010-01-01', 'yyyy-MM-dd', 'UTC'), " +
                        "'UTC', 'Asia/Tokyo') from dual d", Date.class);
        Date result = q.getSingleResult();
        Assert.assertEquals("2010-01-01 09:00:00.000", sdf.format(result));
    }

    @Test
    public void test122_FunctionTimezoneOffset() {
        TypedQuery<Integer> q = em.createQuery("select TIMEZONE_OFFSET('Indian/Cocos', to_date('2010-01-01', " +
                "'yyyy-MM-dd', 'UTC')) from dual d", Integer.class);
        Integer result = q.getSingleResult();
        Assert.assertEquals(Integer.valueOf(390), result);
    }

    @Test
    public void test123_FunctionCurrentDate() {
        TypedQuery<Date> q = em.createQuery("select current_date() from dual d", Date.class);
        Date now = q.getSingleResult();
        Assert.assertTrue(Math.abs(System.currentTimeMillis() - now.getTime()) < 300000L);
    }

    @Test
    public void test124_FunctionCurrentTime() {
        TypedQuery<Date> q = em.createQuery("select current_time() from dual d", Date.class);
        Date now = q.getSingleResult();
        Assert.assertTrue(Math.abs(System.currentTimeMillis() - now.getTime()) < 300000L);
    }

    @Test
    public void test125_FunctionNow() {
        TypedQuery<Date> q = em.createQuery("select now() from dual d", Date.class);
        Date now = q.getSingleResult();
        Assert.assertTrue(Math.abs(System.currentTimeMillis() - now.getTime()) < 300000L);
    }

    @Test
    public void test126_FunctionYear() {
        TypedQuery<Integer> q = em.createQuery("select year(to_date('2017-06-17 12:54:23')) from dual d", Integer.class);
        Integer result = q.getSingleResult();
        Assert.assertEquals(Integer.valueOf(2017), result);
    }

    @Test
    public void test127_FunctionMonth() {
        TypedQuery<Integer> q = em.createQuery("select month(to_date('2017-06-17 12:54:23')) from dual d", Integer.class);
        Integer result = q.getSingleResult();
        Assert.assertEquals(Integer.valueOf(06), result);
    }

    @Test
    public void test128_FunctionWeek() {
        TypedQuery<Integer> q = em.createQuery("select week(to_date('2017-06-17 12:54:23')) from dual d", Integer.class);
        Integer result = q.getSingleResult();
        Assert.assertEquals(Integer.valueOf(24), result);
    }

    @Test
    public void test129_FunctionDayOfYear() {
        TypedQuery<Integer> q = em.createQuery("select dayofyear(to_date('2017-06-17 12:54:23')) from dual d", Integer.class);
        Integer result = q.getSingleResult();
        Assert.assertEquals(Integer.valueOf(168), result);
    }

    @Test
    public void test130_FunctionDayOfMonth() {
        TypedQuery<Integer> q = em.createQuery("select dayofmonth(to_date('2017-06-17 12:54:23')) from dual d", Integer.class);
        Integer result = q.getSingleResult();
        Assert.assertEquals(Integer.valueOf(17), result);
    }

    @Test
    public void test131_FunctionDayOfWeek() {
        TypedQuery<Integer> q = em.createQuery("select dayofweek(to_date('2017-06-17 12:54:23')) from dual d", Integer.class);
        Integer result = q.getSingleResult();
        Assert.assertEquals(Integer.valueOf(6), result);
    }

    @Test
    public void test132_FunctionHour() {
        TypedQuery<Integer> q = em.createQuery("select hour(to_date('2017-06-17 12:54:23')) from dual d", Integer.class);
        Integer result = q.getSingleResult();
        Assert.assertEquals(Integer.valueOf(12), result);
    }

    @Test
    public void test133_FunctionMinute() {
        TypedQuery<Integer> q = em.createQuery("select minute(to_date('2017-06-17 12:54:23')) from dual d", Integer.class);
        Integer result = q.getSingleResult();
        Assert.assertEquals(Integer.valueOf(54), result);
    }

    @Test
    public void test134_FunctionSecond() {
        TypedQuery<Integer> q = em.createQuery("select second(to_date('2017-06-17 12:54:23')) from dual d", Integer.class);
        Integer result = q.getSingleResult();
        Assert.assertEquals(Integer.valueOf(23), result);
    }

    @Test
    public void test135_FunctionMd5() throws Exception {
        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] expected = md.digest("abcdefg".getBytes());
        TypedQuery<byte[]> q = em.createQuery("select md5('abcdefg') from dual d", byte[].class);
        byte[] result = q.getSingleResult();
        Assert.assertTrue(Arrays.equals(expected, result));
    }

    @Test
    public void test136_FunctionRand() {
        TypedQuery<Double> q = em.createQuery("select rand() from dual d", Double.class);
        Double result = q.getSingleResult();
        Assert.assertNotNull(result);
    }

    @Test
    public void test137_FunctionAbs() {
        TypedQuery<Double> q = em.createQuery("select abs(-3.4) from dual d", Double.class);
        Double result = q.getSingleResult();
        Assert.assertEquals(Double.valueOf(Math.abs(-3.4d)), result);
    }

    @Test
    public void test138_FunctionSqrt() {
        TypedQuery<Double> q = em.createQuery("select sqrt(2) from dual d", Double.class);
        Double result = q.getSingleResult();
        Assert.assertEquals(Double.valueOf(Math.sqrt(2)), result);
    }

    @Test
    public void test139_FunctionCbrt() {
        TypedQuery<Double> q = em.createQuery("select cbrt(2) from dual d", Double.class);
        Double result = q.getSingleResult();
        Assert.assertEquals(Double.valueOf(Math.cbrt(2)), result);
    }

    @Test
    public void test140_FunctionExp() {
        TypedQuery<Double> q = em.createQuery("select exp(1.1) from dual d", Double.class);
        Double result = q.getSingleResult();
        Assert.assertEquals(Double.valueOf(Math.exp(1.1d)), result);
    }

    @Test
    public void test141_FunctionPower() {
        TypedQuery<Double> q = em.createQuery("select power(3,2) from dual d", Double.class);
        Double result = q.getSingleResult();
        Assert.assertEquals(Double.valueOf(Math.pow(3, 2)), result);
    }

    @Test
    public void test142_FunctionLn() {
        TypedQuery<Double> q = em.createQuery("select ln(3) from dual d", Double.class);
        Double result = q.getSingleResult();
        Assert.assertEquals(Double.valueOf(Math.log(3)), result);
    }

    @Test
    public void test143_FunctionLog() {
        TypedQuery<Double> q = em.createQuery("select log(3) from dual d", Double.class);
        Double result = q.getSingleResult();
        Assert.assertEquals(Double.valueOf(Math.log10(3)), result);
    }

    @Test
    public void test144_FunctionRound() {
        TypedQuery<Double> q = em.createQuery("select round(3.334) from dual d", Double.class);
        Double result = q.getSingleResult();
        Assert.assertEquals(Double.valueOf(Math.round(3.334d)), result);
    }

    @Test
    public void test145_FunctionCeil() {
        TypedQuery<Double> q = em.createQuery("select ceil(3.334) from dual d", Double.class);
        Double result = q.getSingleResult();
        Assert.assertEquals(Double.valueOf(Math.ceil(3.334d)), result);
    }

    @Test
    public void test146_FunctionFloor() {
        TypedQuery<Double> q = em.createQuery("select floor(3.334) from dual d", Double.class);
        Double result = q.getSingleResult();
        Assert.assertEquals(Double.valueOf(Math.floor(3.334d)), result);
    }

    @Test
    public void test147_FunctionToNumber() {
        TypedQuery<Double> q = em.createQuery("select to_number('3.334') from dual d", Double.class);
        Double result = q.getSingleResult();
        Assert.assertEquals(Double.valueOf(Double.parseDouble("3.334")), result);
    }

    @Test
    public void test148_FunctionSign() {
        TypedQuery<Integer> q = em.createQuery("select sign(-3.334) from dual d", Integer.class);
        Integer result = q.getSingleResult();
        Assert.assertEquals(Integer.valueOf(-1), result);
    }

    @Test
    public void test201_Insert() throws Exception {
        em.getTransaction().begin();

        Employee e = new Employee();
        e.setEmpNo(99999);
        e.setFirstName("John");
        e.setLastName("Doe");
        e.setBirthDate(DATE_FORMATTER.parse("2008-04-12"));
        e.setGender(Gender.MALE);
        e.setHireDate(DATE_FORMATTER.parse("2016-01-30"));
        e.setSalaries(null);
        em.persist(e);

        em.getTransaction().commit();

        TypedQuery<Employee> q = em.createQuery("select e from employee e where e.empNo = :empNo", Employee.class);
        q.setParameter("empNo", 99999);
        Employee inserted = q.getSingleResult();
        Assert.assertEquals(e, inserted);
    }

    @Test(expected=NoResultException.class)
    public void test301_Delete() {
        TypedQuery<Employee> q = em.createQuery("select e from employee e where e.empNo = :empNo", Employee.class);
        q.setParameter("empNo", 10003);
        Employee e = q.getSingleResult();

        em.getTransaction().begin();
        em.remove(e);
        em.getTransaction().commit();

        q = em.createQuery("select e from employee e where e.empNo = :empNo", Employee.class);
        q.setParameter("empNo", 10003);
        e = q.getSingleResult();
        Assert.fail("Employee 10003 was not deleted");
    }

    @Test
    public void test401_Update() {
        TypedQuery<Employee> q = em.createQuery("select e from employee e where e.empNo = :empNo", Employee.class);
        q.setParameter("empNo", 10001);
        Employee e = q.getSingleResult();

        em.getTransaction().begin();

        e.setFirstName("Johny");
        em.persist(e);

        em.getTransaction().commit();

        q = em.createQuery("select e from employee e where e.empNo = :empNo", Employee.class);
        q.setParameter("empNo", 10001);
        Employee updated = q.getSingleResult();
        Assert.assertEquals(e.getFirstName(), updated.getFirstName());
    }

    @Test
    public void test402_UpdateVersionedEntity() {
        TypedQuery<VersionedEntity> q = em.createQuery("select v from versioned v where v.id = :id", VersionedEntity.class);
        q.setParameter("id", 20001);
        VersionedEntity v = q.getSingleResult();

        em.getTransaction().begin();

        v.setField("NEW FIELD VALUE");
        em.persist(v);
        Assert.assertEquals(1, v.getVersion());

        em.getTransaction().commit();

        q = em.createQuery("select v from versioned v where v.id = :id", VersionedEntity.class);
        q.setParameter("id", 20001);
        VersionedEntity updated = q.getSingleResult();
        Assert.assertEquals(v.getField(), updated.getField());
        Assert.assertEquals(2, updated.getVersion());
    }

    @Test
    public void test403_ConcurrentUpdateGeneratesEntityExistsException() {
        // Get the same row twice and detach from Hibernate to simulate two concurrent threads
        TypedQuery<VersionedEntity> q = em.createQuery("select v from versioned v where v.id = :id", VersionedEntity.class);
        q.setParameter("id", 20002);
        VersionedEntity v1 = q.getSingleResult();
        em.detach(v1);

        q = em.createQuery("select v from versioned v where v.id = :id", VersionedEntity.class);
        q.setParameter("id", 20002);
        VersionedEntity v2 = q.getSingleResult();
        em.detach(v2);

        // Update the row
        em.getTransaction().begin();
        v1.setField("NEW VALUE 1");
        em.persist(v1);
        Assert.assertEquals(1, v1.getVersion());
        em.getTransaction().commit();

        // Update the row again, causing an EntityExistsException
        em.getTransaction().begin();
        v2.setField("NEW VALUE 2");
        try {
            thrown.expect(EntityExistsException.class);
            em.persist(v2);
        } finally {
            em.getTransaction().rollback();
        }
    }

    @Test
    public void test501_Sequence() {
        Parameter p = new Parameter();
        p.setValue("x");
        em.getTransaction().begin();
        em.persist(p);
        em.getTransaction().commit();

        p = new Parameter();
        p.setValue("y");
        em.getTransaction().begin();
        em.persist(p);
        em.getTransaction().commit();

        TypedQuery<Parameter> q = em.createQuery("select p from parameter p where p.id = :id", Parameter.class);
        q.setParameter("id", 2L);
        p = q.getSingleResult();
        Assert.assertEquals(p.getValue(), "y");
    }

    private <T extends TimeRange> T findActiveEntity(Collection<T> range) {
        if (range != null) {
            final long now = System.currentTimeMillis();
            for (T t : range) {
                if (t.getFromDate().getTime() <= now && (t.getToDate() == null || now < t.getToDate().getTime())) {
                    return t;
                }
            }
        }
        return null;
    }
}