#import "TGPaintCanvas.h"

#import <OpenGLES/EAGL.h>
#import <OpenGLES/ES2/gl.h>
#import <OpenGLES/ES2/glext.h>
#import "matrix.h"

#import "TGPainting.h"
#import "TGPaintBuffers.h"
#import "TGPaintInput.h"
#import "TGPaintState.h"
#import "TGPaintShader.h"
#import "TGPaintUtils.h"
#import "TGPhotoEditorUtils.h"

#import "TGPaintPanGestureRecognizer.h"

@interface TGPaintCanvas () <UIGestureRecognizerDelegate>
{
    TGPaintBuffers *_buffers;
    CGFloat _screenScale;
    CGAffineTransform _canvasTransform;
    CGRect _dirtyRect;
    
    TGPaintInput *_input;
    TGPaintPanGestureRecognizer *_gestureRecognizer;
    bool _beganDrawing;
    
    __weak dispatch_cancelable_block_t _redrawBlock;
}
@end

@implementation TGPaintCanvas

- (instancetype)initWithFrame:(CGRect)frame
{
    _screenScale = MIN(2.0f, [UIScreen mainScreen].scale);
    
    self = [super initWithFrame:frame];
    if (self != nil)
    {
        self.contentScaleFactor = _screenScale;
        
        _state = [[TGPaintState alloc] init];
        
        [self _setupGestureRecognizers];
    }
    return self;
}

#pragma mark - Painting

- (void)setPainting:(TGPainting *)painting
{
    _painting = painting;
    
    __weak TGPaintCanvas *weakSelf = self;
    painting.contentChanged = ^(CGRect rect)
    {
        __strong TGPaintCanvas *strongSelf = weakSelf;
        if (strongSelf == nil)
            return;
        
        if (!CGRectEqualToRect(rect, CGRectZero))
            strongSelf->_dirtyRect = TGPaintUnionRect(strongSelf->_dirtyRect, rect);
        else
            strongSelf->_dirtyRect = [strongSelf visibleRect];
        
        [strongSelf _scheduleRedraw];
    };
    painting.strokeCommited = ^
    {
        __strong TGPaintCanvas *strongSelf = weakSelf;
        if (strongSelf != nil && strongSelf.strokeCommited != nil)
            strongSelf.strokeCommited();
    };
    
    [self setContext:painting.context];
    [self _updateTransform];
}

- (void)setFrame:(CGRect)frame
{
    [super setFrame:frame];
    
    [self _updateTransform];
}

- (void)_updateTransform
{
    CGAffineTransform transform = CGAffineTransformIdentity;
    
    CGPoint center = TGPaintCenterOfRect(self.bounds);
    CGFloat scale = _painting ? self.bounds.size.width / _painting.size.width : 1.0f;
    if (scale < FLT_EPSILON)
        scale = 1.0f;
    
    transform = CGAffineTransformTranslate(transform, center.x, center.y);
    transform = CGAffineTransformScale(transform, scale, -scale);
    transform = CGAffineTransformTranslate(transform, -self.painting.size.width / 2, -self.painting.size.height / 2);
    
    _canvasTransform = transform;
    _input.transform = transform;
}

#pragma mark - Gesture

- (void)_setupGestureRecognizers
{
    _input = [[TGPaintInput alloc] init];
    
    _gestureRecognizer = [[TGPaintPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
    _gestureRecognizer.delegate = self;
    _gestureRecognizer.minimumNumberOfTouches = 1;
    _gestureRecognizer.maximumNumberOfTouches = 1;
    
    __weak TGPaintCanvas *weakSelf = self;
    _gestureRecognizer.shouldRecognizeTap = ^bool
    {
        __strong TGPaintCanvas *strongSelf = weakSelf;
        if (strongSelf == nil)
            return false;
        
        if (strongSelf.shouldDrawOnSingleTap != nil)
        {
            bool drawOnTap = strongSelf.shouldDrawOnSingleTap();
            bool draw = strongSelf.shouldDraw();
            
            return draw && drawOnTap;
        }
        
        return false;
    };
    [self addGestureRecognizer:_gestureRecognizer];
}

- (void)handlePan:(TGPaintPanGestureRecognizer *)gestureRecognizer
{
    if (gestureRecognizer.state == UIGestureRecognizerStateBegan || gestureRecognizer.state == UIGestureRecognizerStateChanged)
    {
        if (!_beganDrawing)
        {
            [_input gestureBegan:gestureRecognizer];
            if (self.strokeBegan != nil)
                self.strokeBegan();
            _beganDrawing = true;
        }
        else
        {
            [_input gestureMoved:gestureRecognizer];
        }
    }
    else if (gestureRecognizer.state == UIGestureRecognizerStateEnded)
    {
        [_input gestureEnded:gestureRecognizer];
        _beganDrawing = false;
    }
    else if (gestureRecognizer.state == UIGestureRecognizerStateCancelled)
    {
        [_input gestureCanceled:gestureRecognizer];
        _beganDrawing = false;
    }
}

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)__unused gestureRecognizer
{
    if (self.shouldDraw != nil)
        return self.shouldDraw();
    
    return true;
}

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
    if (gestureRecognizer == _gestureRecognizer && ([otherGestureRecognizer isKindOfClass:[UIPinchGestureRecognizer class]] || [otherGestureRecognizer isKindOfClass:[UIRotationGestureRecognizer class]]))
        return false;
    
    return true;
}

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    UIView *view = [super hitTest:point withEvent:event];
    
    if (self.hitTest != nil)
    {
        UIView *maybeHitView = self.hitTest(point, event);
        if (maybeHitView != nil)
            view = maybeHitView;
    }
    
    return view;
}

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)__unused event
{
    return self.pointInsideContainer(point);
}

- (bool)isTracking
{
    return (_gestureRecognizer.state == UIGestureRecognizerStateBegan || _gestureRecognizer.state == UIGestureRecognizerStateChanged);
}

#pragma mark - Draw

- (void)draw
{
    [self drawInRect:[self visibleRect]];
}

- (void)drawInRect:(CGRect)__unused rect
{
    [EAGLContext setCurrentContext:_buffers.context];
    
    glBindFramebuffer(GL_FRAMEBUFFER, _buffers.framebuffer);
    glViewport(0, 0, _buffers.width, _buffers.height);
    
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    
    GLfloat proj[16], effectiveProj[16], final[16];
    mat4f_LoadOrtho(0, (GLfloat)(_buffers.width / _screenScale), 0, (GLfloat)(_buffers.height / _screenScale), -1.0f, 1.0f, proj);
    mat4f_LoadCGAffineTransform(effectiveProj, _canvasTransform);
    mat4f_MultiplyMat4f(proj, effectiveProj, final);
    
    [_painting renderWithProjection:final];
    
    glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
    
    [_buffers present];
    
    TGPaintHasGLError();
    
    _dirtyRect = CGRectZero;
}

- (void)redrawIfNeeded
{
    if (CGRectEqualToRect(_dirtyRect, CGRectZero))
        return;
    
    [self drawInRect:_dirtyRect];
}

- (void)_scheduleRedraw
{
    if (_redrawBlock != nil)
    {
        cancel_block(_redrawBlock);
        _redrawBlock = nil;
    }
    
    __weak TGPaintCanvas *weakSelf = self;
    _redrawBlock = dispatch_after_delay(0.0, [_painting _queue], ^
    {
        __strong TGPaintCanvas *strongSelf = weakSelf;
        if (strongSelf != nil)
            [strongSelf redrawIfNeeded];
    });
}

#pragma mark - 

- (CGRect)visibleRect
{
    return self.bounds;
}

- (void)layoutSubviews
{
    [super layoutSubviews];
    
    [_buffers update];

    [self draw];
}

#pragma mark - GL Setup

- (void)setContext:(EAGLContext *)context
{
    if (context == _buffers.context)
        return;
    
    if (context != nil)
    {
        _buffers = [TGPaintBuffers buffersWithGLContext:context layer:(CAEAGLLayer *)self.layer];
        [_buffers update];
    }
}

+ (Class)layerClass
{
    return [CAEAGLLayer class];
}

#pragma mark -

- (void)setBrush:(TGPaintBrush *)brush
{
    _state.brush = brush;
    [_painting setBrush:brush];
}

- (void)setBrushWeight:(CGFloat)brushWeight
{
    _state.weight = brushWeight;
}

- (void)setBrushColor:(UIColor *)color
{
    _state.color = color;
}

- (void)setEraser:(bool)eraser
{
    _state.eraser = eraser;
}

@end
