//
//  MPExpressionView.m
//  MathPad
//
//  Created by Kim Wittenburg on 17.04.14.
//  Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//

#warning X-Origin is not working yet

#import "MPExpressionView.h"
#import "MPExpressionStorage.h"
#import "MPExpressionLayout.h"

#import "MPRangePath.h"

#import "NSIndexPath+MPAdditions.h"

#import "MPSumFunction.h"

@interface MPExpressionView ()

@property (nonatomic, weak) NSButton *functionsButton;

@property (nonatomic, strong) NSTimer *caretTimer;
@property (nonatomic) NSTimeInterval caretBlinkRate;
@property (nonatomic) BOOL caretVisible;

@property (nonatomic, getter = isSelectionModifyingStart) BOOL selectionModifyingStart;

@end

@interface MPExpressionView (MPDrawing)

- (NSPoint)expressionOrigin;

@end

@interface MPExpressionView (MPSelection)
- (void)restartCaretTimer;
- (void)updateCaret:(NSTimer *)timer;

- (NSRect)selectionRect;

- (void)selectRangePathWithLocation:(NSIndexPath *)location length:(NSUInteger)length;
@end

@implementation MPExpressionView (MPDrawing)

- (NSPoint)expressionOrigin
{
    NSRect expressionBounds = [self.expressionStorage.rootLayout bounds];
    CGFloat y = (self.bounds.size.height - expressionBounds.size.height) / 2 + fabs(expressionBounds.origin.y);
    return NSMakePoint(0, y);
}

@end

@implementation MPExpressionView (MPSelection)

- (void)restartCaretTimer
{
    if (self.caretTimer) {
        if ([self.caretTimer isValid]) {
            [self.caretTimer invalidate];
        }
    }
    self.caretTimer = [NSTimer scheduledTimerWithTimeInterval:self.caretBlinkRate/2 target:self selector:@selector(updateCaret:) userInfo:nil repeats:YES];
    self.caretVisible = NO;
    [self updateCaret:self.caretTimer];
}

- (void)updateCaret:(NSTimer *)timer
{
    self.caretVisible = !self.caretVisible;
    self.needsDisplay = YES;
}

- (NSRect)selectionRect
{
    NSRect selectionRect = [self.expressionStorage.rootLayout boundingRectForRangePath:self.selection];
    if (self.selection.length == 0) {
        selectionRect.size.width = 1;
    }
    return selectionRect;
}

- (void)selectRangePathWithLocation:(NSIndexPath *)location length:(NSUInteger)length
{
    MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:[location indexPathByRemovingLastIndex]];
    NSUInteger locationIndex = location.lastIndex;
    if (locationIndex > targetExpression.length) {
        locationIndex = targetExpression.length;
    }
    NSUInteger lastSelectedIndex = location.lastIndex + length;
    if (lastSelectedIndex > targetExpression.length) {
        lastSelectedIndex = targetExpression.length;
    }
    self.selection = MPMakeRangePath([location indexPathByReplacingLastIndexWithIndex:locationIndex],lastSelectedIndex - locationIndex);
}

@end

@implementation MPExpressionView

#pragma mark Creation Methods
- (id)initWithFrame:(NSRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        [self initializeExpressionView];
    }
    return self;
}

- (id)initWithCoder:(NSCoder *)aDecoder
{
    self = [super initWithCoder:aDecoder];
    if (self) {
        [self initializeExpressionView];
    }
    return self;
}

- (void)initializeExpressionView
{
    // Setup the Expression Storage
    MPExpressionStorage *expressionStorage = [[MPExpressionStorage alloc] initWithElements:@[@"12345", [[MPSumFunction alloc] init], [[MPSumFunction alloc] init]]];
    expressionStorage.expressionView = self;
    _expressionStorage = expressionStorage;
    
    // Setup the Functions Button
    NSButton *button = [[NSButton alloc] initWithFrame:NSZeroRect];
    button.buttonType = NSSwitchButton;
    [button setTitle:@"Functions"];
    self.functionsButton = button;
    [self addSubview:self.functionsButton];
    
    // Setup Selection
    self.selection = [[MPRangePath alloc] initWithRange:NSMakeRange(0, 0)];
    self.caretBlinkRate = 1.0;
    [self restartCaretTimer];
}

#pragma mark Properties
- (void)setExpressionStorage:(MPExpressionStorage *)expressionStorage
{
    _expressionStorage.expressionView = nil;
    _expressionStorage = expressionStorage;;
    _expressionStorage.expressionView = self;
    [self invalidateIntrinsicContentSize];
}

- (void)setSelection:(MPRangePath *)selection
{
    _selection = selection;
    [self restartCaretTimer];
    self.needsDisplay = YES;
}

#pragma mark NSView Stuff
- (BOOL)acceptsFirstResponder
{
    return YES;
}

- (BOOL)canBecomeKeyView
{
    return YES;
}

- (BOOL)isFlipped
{
    return NO;
}

- (BOOL)isOpaque
{
    return YES;
}

- (void)layout
{
    NSSize buttonSize = [self.functionsButton fittingSize];
    self.functionsButton.frame = NSMakeRect(self.bounds.size.width - buttonSize.width,
                                            (self.bounds.size.height - buttonSize.height) / 2,
                                            buttonSize.width,
                                            buttonSize.height);
    [super layout];
}

- (NSSize)intrinsicContentSize
{
//    return NSMakeSize(500, 500);
//    return self.bounds.size;
    return self.expressionStorage.rootLayout.bounds.size;
}

- (void)resetCursorRects
{
    [self addCursorRect:self.bounds
                 cursor:[NSCursor IBeamCursor]];
}

#pragma mark Key Event Handling
- (void)keyDown:(NSEvent *)theEvent
{
    NSString *characters = theEvent.characters;
    NSMutableCharacterSet *allowedCharacters = [NSMutableCharacterSet alphanumericCharacterSet];
    [allowedCharacters addCharactersInString:@"+-*/= "];
    if (characters.length == 1 && [characters stringByTrimmingCharactersInSet:allowedCharacters].length == 0) {
        MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:[self.selection.location indexPathByRemovingLastIndex]];
        [targetExpression replaceSymbolsInRange:self.selection.rangeAtLastIndex withElements:@[characters]];
        self.selection = MPMakeRangePath([self.selection.location indexPathByIncrementingLastIndex], 0);
    } else {
        [self interpretKeyEvents:@[theEvent]];
    }
}

- (void)moveRight:(id)sender
{
    if (self.selection.length > 0) {
        self.selection = MPMakeRangePath(self.selection.maxRangePath, 0);
    } else {
        NSIndexPath *newSelectionLocation = [self.selection.location indexPathByIncrementingLastIndex];
        [self selectRangePathWithLocation:newSelectionLocation
                                   length:0];
    }
}

- (void)moveLeft:(id)sender
{
    if (self.selection.length > 0) {
        self.selection = MPMakeRangePath(self.selection.location, 0);
        [self selectRangePathWithLocation:self.selection.location length:0];
    } else {
        NSUInteger selectionIndex = self.selection.location.lastIndex;
        if (selectionIndex > 0) {
            --selectionIndex;
        }
        NSIndexPath *newSelectionLocation = [self.selection.location indexPathByReplacingLastIndexWithIndex:selectionIndex];
        [self selectRangePathWithLocation:newSelectionLocation
                                   length:0];
    }
}

- (void)moveWordRight:(id)sender
{
    NSIndexPath *location;
    if (self.selection.length > 0) {
        location = self.selection.maxRangePath;
    } else {
        location = self.selection.location;
    }
    MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:[location indexPathByRemovingLastIndex]];
    NSUInteger locationInTarget = NSMaxRange(self.selection.rangeAtLastIndex);
    NSUInteger offset;
    NSUInteger elementIndex = [targetExpression indexOfElementAtSymbolLocation:locationInTarget
                                                                        offset:&offset];
    NSUInteger newLocation = locationInTarget + [targetExpression elementAtIndex:elementIndex].length - offset;
    [self selectRangePathWithLocation:[location indexPathByReplacingLastIndexWithIndex:newLocation]
                               length:0];
}

- (void)moveWordLeft:(id)sender
{
    MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:[self.selection.location indexPathByRemovingLastIndex]];
    NSUInteger offset;
    NSUInteger elementIndex = [targetExpression indexOfElementAtSymbolLocation:self.selection.location.lastIndex offset:&offset];
    NSUInteger newLocation = self.selection.location.lastIndex;
    if (offset > 0) {
        newLocation -= newLocation - offset > 0 ? offset : newLocation;
    } else if (newLocation > 0) {
        newLocation -= [targetExpression elementAtIndex:elementIndex-1].length;
    }
    self.selection = MPMakeRangePath([self.selection.location indexPathByReplacingLastIndexWithIndex:newLocation], 0);
}

- (void)moveToBeginningOfLine:(id)sender
{
    self.selection = MPMakeRangePath([self.selection.location indexPathByReplacingLastIndexWithIndex:0], 0);
}

- (void)moveToEndOfLine:(id)sender
{
    MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:[self.selection.location indexPathByRemovingLastIndex]];
    self.selection = MPMakeRangePath([self.selection.location indexPathByReplacingLastIndexWithIndex:targetExpression.length], 0);
}

- (void)moveLeftAndModifySelection:(id)sender
{
    if (self.selection.length == 0) {
        self.selectionModifyingStart = YES;
    }
    NSUInteger start = self.selection.location.lastIndex;
    NSUInteger length = self.selection.length;
    if (self.selectionModifyingStart) {
        if (start > 0) {
            --start;
            ++length;
        }
    } else {
        --length;
    }
    self.selection = MPMakeRangePath([self.selection.location indexPathByReplacingLastIndexWithIndex:start], length);
}

- (void)moveRightAndModifySelection:(id)sender
{
    if (self.selection.length == 0) {
        self.selectionModifyingStart = NO;
    }
    NSUInteger start = self.selection.location.lastIndex;
    NSUInteger length = self.selection.length;
    if (self.selectionModifyingStart) {
        ++start;
        --length;
    } else {
        ++length;
    }
    [self selectRangePathWithLocation:[self.selection.location indexPathByReplacingLastIndexWithIndex:start]
                               length:length];
}

- (void)moveWordRightAndModifySelection:(id)sender
{
#warning Unimplemented Method
}

- (void)moveWordLeftAndModifySelection:(id)sender
{
#warning Unimplemented Method
}

- (void)selectAll:(id)sender
{
    NSIndexPath *location = [NSIndexPath indexPathWithIndex:0];
    self.selection = MPMakeRangePath(location, self.expressionStorage.length);
}

- (void)deleteBackward:(id)sender
{
    MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:[self.selection.location indexPathByRemovingLastIndex]];
    if (self.selection.length > 0) {
        [targetExpression replaceSymbolsInRange:self.selection.rangeAtLastIndex withElements:@[]];
        self.selection = MPMakeRangePath(self.selection.location, 0);
    } else if (self.selection.location.lastIndex > 0) {
        [targetExpression replaceSymbolsInRange:NSMakeRange(self.selection.location.lastIndex-1, 1) withElements:@[]];
        self.selection = MPMakeRangePath([self.selection.location indexPathByDecrementingLastIndex], 0);
    }
}

#pragma mark Mouse Event Handling
- (void)mouseDown:(NSEvent *)theEvent
{
    NSPoint pointInView = [self convertPoint:theEvent.locationInWindow
                                    fromView:nil];
    NSPoint expressionOrigin = self.expressionOrigin;
    pointInView.x -= expressionOrigin.x;
    pointInView.y -= expressionOrigin.y;
    NSIndexPath *selectionPath = [self.expressionStorage.rootLayout indexPathForMousePoint:pointInView];
    self.selection = MPMakeRangePath(selectionPath, 0);
}

#pragma mark Drawing Methods
- (void)drawRect:(NSRect)dirtyRect
{
    // Draw the background
    [super drawRect:dirtyRect];
    [[NSColor whiteColor] set];
    NSRectFill(dirtyRect);
    
    // Calculate the position of the expression (probably also forces layout of the expression the first time)
    NSPoint expressionOrigin = self.expressionOrigin;
    
    // Draw the selection
    if (self.caretVisible || self.selection.length > 0) {
        if (self.selection.length == 0) {
            [[NSColor blackColor] set];
        } else {
            [[NSColor selectedTextBackgroundColor] set];
        }
        NSAffineTransform *transform = [NSAffineTransform transform];
        [transform translateXBy:expressionOrigin.x
                            yBy:expressionOrigin.y];
        [transform concat];
        NSRectFill([self selectionRect]);
        [transform invert];
        [transform concat];
    }
    
    // Draw the expression
    [[NSColor textColor] set];
    [self.expressionStorage.rootLayout drawAtPoint:expressionOrigin];
}

@end
