import {
    ComponentBind,
    ComponentDetached,
    customElement,
    DOM,
    inject
} from 'aurelia-framework';
import { ResizeEvent } from './resize-event';
import { SplitPane } from './split-pane';
import { setFlexBasis } from './util';
import { EventAggregator } from 'aurelia-event-aggregator';

@inject(DOM.Element, EventAggregator)
@customElement(SplitPaneDivider.TAG_NAME)
export class SplitPaneDivider implements ComponentBind, ComponentDetached {
    static readonly TAG_NAME = 'split-pane-divider';

    static isDivider(element: Element): boolean {
        return element.tagName.toLowerCase() === SplitPaneDivider.TAG_NAME;
    }

    parent!: SplitPane;

    private startCoordinate = 0;
    private previousSiblingSize = 0;
    private nextSiblingSize = 0;

    private readonly element: Element;
    private readonly events: EventAggregator;

    constructor(element: any, events: EventAggregator) {
        this.element = element;
        this.events = events;
    }

    bind(bindingContext: SplitPane): void {
        this.parent = bindingContext;
    }

    detached(): void {
        this.removeListeners();
    }

    mouseDown(event: MouseEvent): void {
        if (event.button === 0 && this.startDrag(event.clientX, event.clientY)) {

            // style rules may have affected the flex-basis of the children,
            // so recalculate them now we are starting a drag
            this.parent.setChildrenFlexBasis();

            DOM.addEventListener('mousemove', this.mouseMove, false);
            DOM.addEventListener('mouseup', this.stopDrag, false);
        }
    }

    touchStart(event: TouchEvent): void {
        let touch = event.touches[0];

        if (this.startDrag(touch.clientX, touch.clientY)) {

            // style rules may have affected the flex-basis of the children,
            // so recalculate them now we are starting a drag
            this.parent.setChildrenFlexBasis();

            DOM.addEventListener('touchmove', this.touchMove, false);
            DOM.addEventListener('touchend', this.stopDrag, false);
        }
    }

    private startDrag(clientX: number, clientY: number): boolean {
        let prev = this.element.previousElementSibling;
        let next = this.element.nextElementSibling;

        if (prev !== null && next !== null) {
            let prevRect = prev.getBoundingClientRect();
            let nextRect = next.getBoundingClientRect();

            switch (this.parent.direction) {
                case 'horizontal':
                    this.startCoordinate = clientX;
                    this.previousSiblingSize = prevRect.width;
                    this.nextSiblingSize = nextRect.width;
                    this.events.publish('split-pane:start');
                    return true;

                case 'vertical':
                    this.startCoordinate = clientY;
                    this.previousSiblingSize = prevRect.height;
                    this.nextSiblingSize = nextRect.height;
                    this.events.publish('split-pane:start');
                    return true;

                default:
                    return false;
            }
        } else {
            return false;
        }
    }

    private mouseMove = (event: MouseEvent): void => {
        this.drag(event.clientX, event.clientY);
    };

    private touchMove = (event: TouchEvent): void => {
        event.preventDefault(); // stop page from scrolling

        let touch = event.touches[0];
        this.drag(touch.clientX, touch.clientY);
    };

    private stopDrag = (): void => {
        this.events.publish('split-pane:stop');
        this.removeListeners();
    };

    private drag(clientX: number, clientY: number): void {
        let currentCoordinate = this.parent.direction === 'horizontal' ? clientX : clientY;
        let delta = currentCoordinate - this.startCoordinate;

        let prev = this.element.previousElementSibling;
        let next = this.element.nextElementSibling;

        if (prev !== null && next !== null) {
            let prevSize: number;
            let nextSize: number;

            if (delta < 0) {
                prevSize = Math.max(0, this.previousSiblingSize + delta);
                nextSize = this.nextSiblingSize - (prevSize - this.previousSiblingSize);
            } else if (delta > 0) {
                nextSize = Math.max(0, this.nextSiblingSize - delta);
                prevSize = this.previousSiblingSize - (nextSize - this.nextSiblingSize);
            } else {
                return;
            }

            setFlexBasis(prev, prevSize);
            setFlexBasis(next, nextSize);

            let resizeEvent: ResizeEvent = {
                clientX,
                clientY
            };

            this.events.publish('split-pane:resize', resizeEvent);
        }
    }

    private removeListeners() {
        DOM.removeEventListener('mousemove', this.mouseMove, false);
        DOM.removeEventListener('mouseup', this.stopDrag, false);
        DOM.removeEventListener('touchmove', this.touchMove, false);
        DOM.removeEventListener('touchend', this.stopDrag, false);
    }
}
