//
//  NJRWindowMagnetism.m
//  HostLauncher
//
//  Created by Nicholas Riley on Sun Jun 02 2002.
//  Copyright (c) 2002 Nicholas Riley. All rights reserved.
//

#import "NJRWindowMagnetism.h"
#include <unistd.h>

#define OUTSIDE_THRESHOLD -150
#define INSIDE_THRESHOLD 35
#define MOVE_INTERVAL 1

@implementation NJRWindowMagnetism

- (id)initForWindow:(NSWindow *)window;
{
    if ( (self = [super init]) != nil) {
        [[NSNotificationCenter defaultCenter] addObserver: self selector:@selector(windowMoved:)
                                                     name: NSWindowDidMoveNotification
                                                   object: window];
        attractsScreenEdges = YES;
    }
    return self;
}

- (void)dealloc;
{
    [[NSNotificationCenter defaultCenter] removeObserver: self];
    [super dealloc];
}

- (void)windowMoved:(NSNotification *)notification;
{
    NSWindow *window = [notification object];
    NSPoint startPoint = [window frame].origin, endPoint = [window frame].origin;
    float windowX = [window frame].origin.x;
    float windowY = [window frame].origin.y;
    float windowW = [window frame].size.width;
    float screenX = [[window screen] visibleFrame].origin.x;
    float screenW = [[window screen] visibleFrame].size.width;

    if ([self attractsScreenEdges] && (inMotion == NO)) {
        inMotion = YES;

        if (windowY < ([[window screen] visibleFrame].origin.y + INSIDE_THRESHOLD)
            && windowY > ([[window screen] visibleFrame].origin.y + OUTSIDE_THRESHOLD))
            endPoint.y = [[window screen] visibleFrame].origin.y; /* Bottom of the screen */

        if (windowX < ([[window screen] visibleFrame].origin.x + INSIDE_THRESHOLD)
            && windowX > ([[window screen] visibleFrame].origin.x + OUTSIDE_THRESHOLD))
            endPoint.x = [[window screen] visibleFrame].origin.x; /* Left hand side of the screen */

        if ((windowX > (screenX + screenW - windowW - INSIDE_THRESHOLD))
            && (windowX < (screenX + screenW - windowW - OUTSIDE_THRESHOLD)))
            endPoint.x = (screenX + screenW - windowW); /* Right hand side of the screen */

        if (!NSEqualPoints(startPoint, endPoint))
            [self animateWindow: window motionToFrameOrigin: endPoint];

        inMotion = NO;
    }
}

- (void)animateWindow:(NSWindow *)window motionToFrameOrigin:(NSPoint)newOrigin;
{
    float animationSteps;
    NSPoint originDelta, oldOrigin = [window frame].origin;

    // Calculate the overall distance.
    originDelta.x = newOrigin.x - oldOrigin.x;
    originDelta.y = newOrigin.y - oldOrigin.y;

    // Calculate the number of steps. The inner sqrt finds the distance. The outer sqrt gives us the duration based on the distance. Then we round the time for the loop below.
    animationSteps = round(sqrt(sqrt(abs((originDelta.x * originDelta.x) + (originDelta.y * originDelta.y)))));

    // Divide the distance by the number of steps to get our move originDelta.
    originDelta.x /= animationSteps;
    originDelta.y /= animationSteps;

    // We will move by the originDelta up to the last step or fraction thereof.
    while ( (animationSteps--) > 1.0) {
        oldOrigin.x += originDelta.x;
        oldOrigin.y += originDelta.y;

        [window setFrameOrigin: oldOrigin];
        usleep(MOVE_INTERVAL);
    }

    // To make sure we end up in the correct place we do the last move out of the loop and use the absolute position. That way we have no rounding errors.
    [window setFrameOrigin: newOrigin];
    usleep(MOVE_INTERVAL);
}

- (void)setAttractsScreenEdges:(BOOL)yn;
{
    attractsScreenEdges = yn;
}

- (BOOL)attractsScreenEdges;
{
    return attractsScreenEdges;
}

@end
