//
//  StageView.m
//  OminoGobo
//
//  Created by David Van Brink on 12/29/13.
//  Copyright (c) 2013 omino. All rights reserved.
//

#import "StageView2.h"
#import "BuildSpotlightImages.h"
#import <QuartzCore/QuartzCore.h>
#import "OminoGoboMessages.h"
#import "OmGraphics.h"
#include <stdlib.h>


#define kSpotlightDiameter 32  // size of the offscreen spotlight images

static int rr(int max)
{
    unsigned long k = rand();
    int result = k % max;
    return result;
}

static int randomSaturatedColor()
{
    int parts[3];
    for(int i = 0; i < 3; i++)
        parts[i] = rr(256);
    int fullIx = rr(3);
    int zeroIx = rr(2);
    if(zeroIx >= fullIx)
        zeroIx++;
    parts[fullIx] = 255;
    parts[zeroIx] = 0;
    int result = (parts[0] << 16) | (parts[1] << 8) | parts[2];
    return result;
}

@implementation TouchInProgress2
// we keep these in a dictionary of touches-in-progress to keep track
// of our dragons.
@end

@implementation StageView2

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    
    [self setMultipleTouchEnabled:YES];
    
    int spotlightDiameter = kSpotlightDiameter;

    // add the spotlight layers. experiment.
    self->spotlightLayers = [[NSMutableArray alloc] init];
    CGRect spotlightFrame = CGRectMake(0,0,spotlightDiameter,spotlightDiameter);
    for(int i = 0; i < kSpotlightCount; i++)
    {
        CGImageRef spotlightImage = [BuildSpotlightImages buildSpotlight:spotlightDiameter fuzzy:(int)(spotlightDiameter * 0.5) color:randomSaturatedColor() kind:0];

        CALayer *sublayer = [CALayer layer];
        sublayer.opacity = 1;
        sublayer.frame = spotlightFrame;
        sublayer.contents = (__bridge id)spotlightImage;
//        sublayer.edgeAntialiasingMask = kCALayerLeftEdge | kCALayerRightEdge | kCALayerBottomEdge | kCALayerTopEdge;
        [self->spotlightLayers addObject:sublayer];
        [self.layer addSublayer:sublayer];
    }
    
    {
        // add the fadeout soft frame
        CGImageRef borderFrameImage = [BuildSpotlightImages buildSoftFrame:self.frame.size border:20];
        CALayer *sublayer = [CALayer layer];
        sublayer.opacity = 1;
        
        sublayer.frame = self.frame;
        sublayer.contents = (__bridge id)borderFrameImage;

        [self.layer addSublayer:sublayer];
    }
    
    [self addHardcodedMediaLayer];
    
    self.layer.masksToBounds = YES;
    
    self->touchIdToSpotlightIndex = [[NSMutableDictionary alloc]init];
    for(int i = 0; i < kSpotlightCount; i++)
        self->spotlightBeingDragged[i] = NO;
    
    return self;
}

-(void)replay:(NSTimer *)timer
{
    [avPlayer play];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
                        change:(NSDictionary *)change context:(void *)context
{
    if ([keyPath isEqualToString:@"readyForDisplay"])
    {
        layerPlayer.frame = layerPlayer.videoRect;
        layerPlayer.position = CGPointMake(gBounds.size.width/2, gBounds.size.height/2);
        layerPlayer.opacity = .5;
    }
}

-(void)addHardcodedMediaLayer
{
    const char *urlS = "http://omino.com/art/img/fireplace.m4v";
    NSString *urlNs = [NSString stringWithCString:urlS encoding:NSASCIIStringEncoding];
    NSURL *url = [NSURL URLWithString:urlNs];
    self->avPlayer = [AVPlayer playerWithURL:url];
    
    
    self->layerPlayer = [[AVPlayerLayer alloc] init];
    layerPlayer.player = avPlayer;
    
    CGRect re = self.frame;
    re.size.width /= 3.0;
    layerPlayer.frame = re;
    //layerPlayer.bounds = layerPlayer.videoRect;
    layerPlayer.opacity = 0.0;
    [self.layer addSublayer:layerPlayer];
    
    [layerPlayer setMasksToBounds:YES];
    [layerPlayer setCornerRadius:25.0]; // not relative to window size
    [layerPlayer setBorderWidth:10.0];
    [layerPlayer setBorderColor:[[UIColor darkGrayColor] CGColor]];

    
    CGImageRef maskImage = [BuildSpotlightImages buildSpotlight:100 fuzzy:(int)(20 * 0.5) color:randomSaturatedColor() kind:0];
    
    CALayer *maskLayer = [CALayer layer];
    maskLayer.opacity = 1;
    maskLayer.frame = re;
    maskLayer.contents = (__bridge id)maskImage;

    layerPlayer.mask = maskLayer;

    [NSTimer scheduledTimerWithTimeInterval:0.21 target:self selector:@selector(replay:) userInfo:layerPlayer repeats:YES];
    [self->layerPlayer addObserver:self forKeyPath:@"readyForDisplay" options:0 context:NULL];
}

-(void)setGoboModel:(GoboModel *) model
{
    if(self->goboModel)
    {
        [self->goboModel removeListener:self];
    }

    self->goboModel = model;
    [self->goboModel addListener:self];
}

- (void)omDrawRect:(CGRect)rect
{
    [self gSetFillColor:0];
    [self gFillRect:rect];
    if(self.isEditingStage)
    {
        CGRect re = gBounds;
        
        [self gSetStrokeColor:0x101010];
        [self gMoveToX:0.0 y:0.0];
        [self gLineToX:re.size.width y:re.size.height];
        [self gMoveToX:gWidth y:0.0];
        [self gLineToX:0 y:re.size.height];
        
        // grid
        for(CGFloat i = 0; i <= 15; i++)
        {
            CGFloat wi = 1;
            int j = i + 6;
            if(j % 3 == 0)
                wi += .5;
            if(j % 9 == 0)
                wi += 1;
            [self gSetStrokeWidth:wi];
            CGFloat x = gBounds.size.width * (i + .5) / 16;
            [self gMoveToX:x y:0];
            [self gLineToX:x y:gBounds.size.height];
        }
        for(int j = 0; j <= 9; j++)
        {
            CGFloat wi = 1;
            if(j % 3 == 0)
                wi += .5;
            if(j % 9 == 0)
                wi += 1;
            [self gSetStrokeWidth:wi];
            CGFloat y = gBounds.size.height * j / 9;
            [self gMoveToX:0 y:y];
            [self gLineToX:gBounds.size.width y:y];
        }
    }
    
    [self updateSpotlightLayers];
}


- (CGPoint) spotlightCenter:(GoboSpotlight *)gs
{
    CGPoint gsCenter = CGPointMake(self->gWidth * gs->pos.x, self->gHeight * gs->pos.y);
    return gsCenter;
}

// distance to spotlight on screen, or -1 if we're not even on the spotlight.
- (CGFloat) distanceToSpotlight:(GoboSpotlight *)gs point:(CGPoint)p
{
    // find the rect
    CGPoint gsCenter = [self spotlightCenter:gs];
    CGFloat diameter = self->gWidth * gs->diameter;
    
    CGFloat minimumTouchableDiameter = 30;
    if(diameter < minimumTouchableDiameter)
        diameter = minimumTouchableDiameter; // minimum selectable size, thx...
    CGFloat radius = diameter / 2.0;
    CGRect gsRect = CGRectMake(gsCenter.x - radius, gsCenter.y - radius, diameter, diameter);
    
    CGFloat distance = -1;
    if(CGRectContainsPoint(gsRect,p))
    {
        // ok, the pixel distance.
        CGFloat dx = p.x - gsCenter.x;
        CGFloat dy = p.y - gsCenter.y;
        distance = sqrt(dx * dx + dy * dy);
        
        // Now. We treat a hit within some threshold as "perfect".
        // later "perfect" hits can still take precedence (higher spotlights)
        // this help select by visible layer correctly.
        if(distance < diameter/6)
            distance = 0;

        // Round spotlights only (today), so... dont hit on corners.
        if(distance > diameter/2)
            distance = -1;
    }
    return distance;
}

- (int)whichSpotlightTouched:(CGPoint)p
{
    if(!self->goboModel)
        return -1;

    CGFloat closestDistance = 100000000;
    int closestIndex = -1;
    
    // higher spotlight numbers are in front...
    for(int i = 0; i < kSpotlightCount; i++)
    {
        // we skip spotlights already being dragged, as well...
        
        if(self->spotlightBeingDragged[i])
            continue;

        GoboSpotlight *gs = [self->goboModel getSpotlight:i];
        if(!gs->inUse)
            continue;
        
        // if soloing, only get the one they clicked.
        if(self->soloSpotlightOn && i != self->soloSpotlightNumber)
            continue;
        
        CGFloat dist = [self distanceToSpotlight:gs point:p];
        if(dist < 0)
            continue;
        if(dist > closestDistance)
            continue;
        closestDistance = dist;
        closestIndex = i;
    }
    
    return closestIndex;
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    if(!self->goboModel)
        return;

    for(UITouch *t in [touches allObjects])
    {
        CGPoint p = [t locationInView:self];
        int spotlightNumber = [self whichSpotlightTouched:p];

        if(spotlightNumber < 0)
        {
            // create a new one, unless we've been soloing...
            if(self->soloSpotlightOn)
                continue;

            // a new one! for now, this always means create a new spotlight.
            spotlightNumber = [self->goboModel freshSpotlight];
            if(spotlightNumber < 0)
                continue;
            [self->goboModel setSpotlight:spotlightNumber color:0 diameter:.15 x:p.x / self->gWidth y:p.y / self->gHeight];
            
            [self->goboModel setSpotlightSaturation:spotlightNumber saturation:.5];
            [self->goboModel setSpotlightHue:spotlightNumber hue:(random() & 0xff) / 255.0];
            // and now that we've "found" the new spotlight, it's just like any other Hit.
        }

        TouchInProgress2 *tip = [[TouchInProgress2 alloc] init];
        tip->identity = [NSString stringWithFormat:@"%lld",(long long)t];
        tip->spotlightIndex = spotlightNumber;
        GoboSpotlight *gs = [self->goboModel getSpotlight:spotlightNumber];
        CGPoint gsCenter = [self spotlightCenter:gs];
        tip->touchPoint = CGPointMake(p.x - gsCenter.x, p.y - gsCenter.y);
        [self->touchIdToSpotlightIndex setObject:tip forKey:tip->identity];
        [OmBus omBusMessage:eSelectSpotlight double:spotlightNumber];
    }
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    if(!self->goboModel)
        return;
    for(UITouch *t in [touches allObjects])
    {
        NSString *tId = [NSString stringWithFormat:@"%lld",(long long)t];
        TouchInProgress2 *tip = self->touchIdToSpotlightIndex[tId];
        if(tip)
        {
            CGPoint p = [t locationInView:self];
            p.x -= tip->touchPoint.x;
            p.y -= tip->touchPoint.y;
            p.x /= self->gWidth;
            p.y /= self->gHeight;
            [self->goboModel moveSpotlight:tip->spotlightIndex x:p.x y:p.y];
        }

    }
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    if(!self->goboModel)
        return;
    for(UITouch *t in [touches allObjects])
    {
        NSString *tId = [NSString stringWithFormat:@"%lld",(long long)t];
        TouchInProgress2 *tip = self->touchIdToSpotlightIndex[tId];
        if(tip)
        {
            self->spotlightBeingDragged[tip->spotlightIndex] = NO;
            //[self->goboModel setSpotlightDiameter:tip->spotlightIndex diameter:.01];
            [self->touchIdToSpotlightIndex removeObjectForKey:tId];
            
        }
    }
}

-(void)setDefaultDiameter:(CGFloat)newDefaultDiameter
{
    self->defaultDiameter = newDefaultDiameter;
}

-(void) updateSpotlightLayers
{
    if(!self->goboModel)
        return;
    for(int ix = 0; ix < kSpotlightCount; ix++)
    {
        GoboSpotlight *gs = [self->goboModel getSpotlight:ix];
        CGFloat diameter = gs->diameter * gWidth;
        CGPoint newCenter = CGPointMake(gs->pos.x * gWidth, gs->pos.y * gHeight);

        CALayer *x = ((CALayer *)self->spotlightLayers[ix]);
        int layerDiameter = x.frame.size.width;
        layerDiameter = kSpotlightDiameter;
        
        [CATransaction begin];
        [CATransaction setValue: [NSNumber numberWithBool: YES] forKey: kCATransactionDisableActions];
        
        CATransform3D t = CATransform3DMakeScale(diameter / layerDiameter, diameter / layerDiameter, 1);
        x.transform = t;
        
        // if soloing, make all the other lights dimmer.
        CGFloat opacity = gs->opacity;
        if(self->soloSpotlightOn)
        {
            if(ix == self->soloSpotlightNumber)
            {
                if(opacity < .15)
                    opacity = .15; // minimum visibility for solo'd lamp
            }
            else
                opacity = fmin(opacity,0.1);
        }
        x.opacity = opacity;
        x.position = newCenter;
        
        [CATransaction commit];
    }

}

-(void)spotlightsChanged
{
    [self updateSpotlightLayers];
//    [self setNeedsDisplay];
}

// OmBus message receivers
-(void) omBusMessage:(int)messageType double:(double)doubleValue
{
    int spotlightNumber;
    int paramNumber = 0;
    [OminoGoboMessages isSpotlightMessage:messageType spotlightNumberOut:&spotlightNumber paramNumberOut:&paramNumber];
    
    switch(paramNumber)
    {
        case eSpotlightHue:
        case eSpotlightSaturation:
        case eSpotlightKind:
        {
            GoboSpotlight *gs = [self->goboModel getSpotlight:spotlightNumber];
            int co = [OmGraphics rgbFromHue:gs->hue saturation:gs->saturation brightness:1.0];
            CGImageRef spotlightImage = [BuildSpotlightImages buildSpotlight:kSpotlightDiameter fuzzy:(kSpotlightDiameter * 0.5) color:co kind:gs->kind];
            CALayer *spotlightLayer = self->spotlightLayers[spotlightNumber];
            spotlightLayer.contents = (__bridge id)spotlightImage;
            break;
        }
            
        case eSpotlightSolo:
        {
            if(self.isEditingStage)
                [self soloSpotlight:spotlightNumber solo:doubleValue > 0];
        }
    }
}
-(void) omBusMessage:(int)messageType string:(const char *)stringValue {}
-(void) omBusMessage:(int)messageType dictionary:(NSDictionary *)dictionaryVaue {}

-(void) soloSpotlight:(int)spotlightNumber solo:(BOOL)soloOn
{
    self->soloSpotlightOn = soloOn;
    self->soloSpotlightNumber = spotlightNumber;
    [self updateSpotlightLayers];
}

@end
