// adapted from draggable.js Scrollable plugin (MIT)
// https://github.com/Shopify/draggable/blob/master/src/Draggable/Plugins/Scrollable/Scrollable.js
import UAParser from 'ua-parser-js';
import {
    getDocumentScrollingElement,
    getParentScrollableElement
} from './utils';

export const defaultOptions = {
    speed: 8,
    sensitivity: 50
};

export default class ScrollHandler {
    constructor() {
        this.options = Object.assign({}, defaultOptions);

        this.currentMousePosition = null;
        this.findScrollableElementFrame = null;
        this.scrollableElement = null;
        this.scrollAnimationFrame = null;

        // bind `this` so methods can be passed to requestAnimationFrame
        this._scroll = this._scroll.bind(this);

        // cache browser info to avoid parsing on every animation frame
        this.userAgent = new UAParser();
    }

    dragStart(draggableInfo) {
        this.findScrollableElementFrame = requestAnimationFrame(() => {
            this.scrollableElement = this.getScrollableElement(draggableInfo.element);
        });
    }

    dragMove(draggableInfo) {
        this.findScrollableElementFrame = requestAnimationFrame(() => {
            this.scrollableElement = this.getScrollableElement(draggableInfo.target);
        });

        if (!this.scrollableElement) {
            return;
        }

        this.currentMousePosition = {
            clientX: draggableInfo.mousePosition.x,
            clientY: draggableInfo.mousePosition.y
        };

        this.scrollAnimationFrame = requestAnimationFrame(this._scroll);
    }

    dragStop() {
        cancelAnimationFrame(this.scrollAnimationFrame);
        cancelAnimationFrame(this.findScrollableElementFrame);

        this.currentMousePosition = null;
        this.findScrollableElementFrame = null;
        this.scrollableElement = null;
        this.scrollAnimationFrame = null;
    }

    getScrollableElement(target) {
        let scrollableElement = getParentScrollableElement(target);

        // workaround for our particular scrolling setup
        // TODO: find a way to make this configurable
        if (scrollableElement === getDocumentScrollingElement()) {
            scrollableElement = document.querySelector('.gh-koenig-editor');
        }

        return scrollableElement;
    }

    _scroll() {
        if (!this.scrollableElement || !this.currentMousePosition) {
            return;
        }

        cancelAnimationFrame(this.scrollAnimationFrame);

        let {speed, sensitivity} = this.options;

        let rect = this.scrollableElement.getBoundingClientRect();

        let scrollableElement = this.scrollableElement;
        let clientX = this.currentMousePosition.clientX;
        let clientY = this.currentMousePosition.clientY;

        let {offsetHeight, offsetWidth} = scrollableElement;

        let topPosition = rect.top + offsetHeight - clientY;
        let bottomPosition = clientY - rect.top;
        let isSafari = this.userAgent.getBrowser().name === 'Safari';

        // Safari will automatically scroll when the mouse is outside of the window
        // so we want to avoid our own scrolling in that situation to avoid jank
        if (topPosition < sensitivity && !(isSafari && topPosition < 0)) {
            scrollableElement.scrollTop += speed;
        } else if (bottomPosition < sensitivity && !(isSafari && bottomPosition < 0)) {
            scrollableElement.scrollTop -= speed;
        }

        if (rect.left + offsetWidth - clientX < sensitivity) {
            scrollableElement.scrollLeft += speed;
        } else if (clientX - rect.left < sensitivity) {
            scrollableElement.scrollLeft -= speed;
        }

        this.scrollAnimationFrame = requestAnimationFrame(this._scroll);
    }
}
