import { IEventManager } from "../Events/EventManager";
import { DiagramMouseEvent, MouseEventSource, MouseButton, MouseEventElementType, DiagramWheelEvent, IMouseOperationsListener, DiagramContextMenuEvent, DiagramMouseEventTouch } from "../Events/Event";
import { EvtUtils } from "@devexpress/utils/lib/utils/evt";
import { Rectangle } from "@devexpress/utils/lib/geometry/rectangle";
import { Offsets } from "@devexpress/utils/lib/geometry/offsets";
import { DomUtils } from "@devexpress/utils/lib/utils/dom";
import { Size } from "@devexpress/utils/lib/geometry/size";
import { Point } from "@devexpress/utils/lib/geometry/point";

import { KeyUtils } from "@devexpress/utils/lib/utils/key";
import { Browser } from "@devexpress/utils/lib/browser";
import { RenderUtils, raiseEvent } from "./Utils";
import { CanvasItemsManager } from "./CanvasItemsManager";
import { IScrollView, NativeScrollView } from "./ScrollView";
import { IReadOnlyChangesListener, AutoZoomMode } from "../Settings";
import { InputManager } from "./InputManager";
import { CanvasPageManager, ICanvasPageManagerSettings } from "./CanvasPageManager";
import { CanvasViewManager } from "./CanvasViewManager";
import { CanvasSelectionManager } from "./CanvasSelectionManager";
import { AutoScrollController } from "./AutoScrollController";
import { TextMeasurer } from "./Measurer/TextMeasurer";
import { DiagramItem } from "../Model/DiagramItem";
import { RenderHelper } from "./RenderHelper";
import { DOMManipulator } from "./DOMManipulator";
import { ITextMeasurer } from "./Measurer/ITextMeasurer";
import { EventUtils } from "../Utils";

const READONLY_CSSCLASS = "dxdi-read-only";
const TOUCH_ACTION_CSSCLASS = "dxdi-touch-action"; 
export const LONG_TOUCH_TIMEOUT = 500;
export const DBL_CLICK_TIMEOUT = 500;

export class RenderManager implements IReadOnlyChangesListener, IMouseOperationsListener {
    view: CanvasViewManager;
    input: InputManager;
    items: CanvasItemsManager;
    page: CanvasPageManager;
    contextMenuEnabled: boolean;
    selection: CanvasSelectionManager;
    measurer: ITextMeasurer;
    dom: DOMManipulator;

    private moveLocked: boolean = false;
    private lockMouseMoveTimer: number = -1;
    private scroll: IScrollView;
    private autoScroll: AutoScrollController;
    private mainElement: HTMLElement;
    private svgElement: SVGSVGElement;
    private events: IEventManager;
    private lastDownMouseEvent: DiagramMouseEvent;
    private lastClickElement = undefined;
    private longTouchTimer: number = undefined;
    private dblTouchTimer: number = undefined;
    private touchDownPoint: Point;

    static touchPositionLimit = 4;

    private pointers:Record<string, any> = { };

    private onPointerDownHandler: any;
    private onPointerUpHandler: any;
    private onPointerMoveHandler: any;
    private onPointerCancelHandler: any;
    private onPointerLeaveHandler: any;

    private onSvgMouseMoveHandler: any;
    private onSvgMouseDownHandler: any;
    private onMouseDblClickHandler: any;
    private onMouseWheelHandler: any;
    private onContextMenuHandler: any;
    private onWindowResizelHandler: any;
    private onOrientationChangeHandler: any;
    private onMouseClickHandler: any;

    private instanceId: string;

    constructor(parent: HTMLElement, events: IEventManager, measurer: ITextMeasurer, settings: ICanvasPageManagerSettings, instanceId: string, scrollView?: IScrollView,
        focusElementsParent?: HTMLElement) {
        const mainElement = RenderHelper.createMainElement(parent);
        const svgElement = RenderHelper.createSvgElement(mainElement);

        this.instanceId = instanceId;
        this.scroll = scrollView || new NativeScrollView(parent);
        this.measurer = measurer;
        this.dom = new DOMManipulator(this.measurer);

        this.view = new CanvasViewManager(this.scroll, svgElement, settings.modelSize, settings.zoomLevel, settings.autoZoom, settings.simpleView, settings.rectangle, this.dom, this.instanceId);
        this.input = new InputManager(mainElement, this.view, events, this.measurer, settings.zoomLevel, focusElementsParent);
        this.items = new CanvasItemsManager(this.view.canvasElement, settings.zoomLevel, this.dom, this.instanceId);
        this.page = new CanvasPageManager(this.view.pageElement, settings, this.dom, this.instanceId);
        this.selection = new CanvasSelectionManager(this.view.canvasElement, settings.zoomLevel, settings.readOnly, this.dom, this.instanceId);
        this.contextMenuEnabled = settings.contextMenuEnabled;

        this.view.onViewChanged.add(this.page);
        this.view.onViewChanged.add(this.items);
        this.view.onViewChanged.add(this.selection);
        this.view.onViewChanged.add(this.input);

        this.autoScroll = new AutoScrollController(this.scroll, svgElement, this.view, this.dom);

        this.attachEvents(svgElement);

        this.mainElement = mainElement;
        this.svgElement = svgElement;
        this.events = events;

        this.notifyReadOnlyChanged(settings.readOnly);
    }
    clean(removeElement?: (element: HTMLElement) => void): void {
        this.killLockMouseMoveTimer();
        this.clearLastMouseDownEvent();

        this.detachEvents(this.svgElement);
        this.scroll.detachEvents();
        this.input.detachEvents();
        this.dom.cancelAnimation();
        if(removeElement)
            removeElement(this.mainElement);
    }

    replaceParent(parent: HTMLElement, scroll?: IScrollView): void {
        if(this.mainElement && this.mainElement.parentNode !== parent)
            parent.appendChild(this.mainElement);
        if(scroll && scroll !== this.scroll) {
            this.scroll && this.scroll.detachEvents();
            this.scroll = scroll;
        }
        if(this.measurer instanceof TextMeasurer)
            this.measurer.replaceParent(parent);
    }
    update(saveScrollPosition: boolean): void {
        this.view.adjust({ horizontal: !saveScrollPosition, vertical: !saveScrollPosition });
        this.page.redraw();
    }
    onNewModel(items: DiagramItem[]): void {
        this.measurer.onNewModel(items, this.dom);
    }
    clear(): void {
        this.items.clear();
        this.selection.clear();
        this.input.clear();
    }
    protected attachPointerEvents(svgElement: SVGSVGElement): void {
        DomUtils.addClassName(svgElement, TOUCH_ACTION_CSSCLASS);

        RenderHelper.addEventListener(svgElement, "pointerdown", this.onPointerDownHandler);
        RenderHelper.addEventListener(svgElement, "mousedown", this.onSvgMouseDownHandler);
        RenderHelper.addEventListener(svgElement, "mousemove", this.onSvgMouseMoveHandler);
        RenderHelper.addEventListener(Browser.TouchUI ? svgElement : document, "pointerup", this.onPointerUpHandler);
        RenderHelper.addEventListener(Browser.TouchUI ? svgElement : document, "pointermove", this.onPointerMoveHandler);

        RenderHelper.addEventListener(svgElement, "pointercancel", this.onPointerCancelHandler);
        RenderHelper.addEventListener(svgElement, "pointerleave", this.onPointerLeaveHandler);
    }
    protected detachPointerEvents(svgElement: SVGSVGElement): void {
        RenderHelper.removeEventListener(svgElement, "pointerdown", this.onPointerDownHandler);
        RenderHelper.removeEventListener(svgElement, "mousedown", this.onSvgMouseDownHandler);
        RenderHelper.removeEventListener(svgElement, "mousemove", this.onSvgMouseMoveHandler);
        RenderHelper.removeEventListener(Browser.TouchUI ? svgElement : document, "pointerup", this.onPointerUpHandler);
        RenderHelper.removeEventListener(Browser.TouchUI ? svgElement : document, "pointermove", this.onPointerMoveHandler);
        RenderHelper.removeEventListener(svgElement, "pointercancel", this.onPointerCancelHandler);
        RenderHelper.removeEventListener(svgElement, "pointerleave", this.onPointerLeaveHandler);

        DomUtils.removeClassName(svgElement, TOUCH_ACTION_CSSCLASS);
    }

    private attachEvents(svgElement: SVGSVGElement) {
        this.onPointerDownHandler = this.onPointerDown.bind(this);
        this.onPointerUpHandler = this.onPointerUp.bind(this);
        this.onPointerMoveHandler = this.onPointerMove.bind(this);
        this.onPointerCancelHandler = this.onPointerCancel.bind(this);
        this.onPointerLeaveHandler = this.onPointerLeave.bind(this);

        this.onSvgMouseDownHandler = this.onSvgMouseDown.bind(this);
        this.onSvgMouseMoveHandler = this.onSvgMouseMove.bind(this);

        this.onMouseWheelHandler = this.onMouseWheel.bind(this);
        this.onMouseDblClickHandler = this.onMouseDblClick.bind(this);
        this.onContextMenuHandler = this.onContextMenu.bind(this);
        this.onWindowResizelHandler = this.onWindowResize.bind(this);
        this.onOrientationChangeHandler = this.onOrientationChange.bind(this);
        this.onMouseClickHandler = this.onMouseClick.bind(this);

        this.attachPointerEvents(svgElement);

        RenderHelper.addEventListener(svgElement, "wheel", this.onMouseWheelHandler);
        RenderHelper.addEventListener(svgElement, "dblclick", this.onMouseDblClickHandler);
        RenderHelper.addEventListener(svgElement, "click", this.onMouseClickHandler);
        RenderHelper.addEventListener(svgElement, "contextmenu", this.onContextMenuHandler);
        RenderHelper.addEventListener(window, "resize", this.onWindowResizelHandler);
        RenderHelper.addEventListener(window, "orientationchange", this.onOrientationChangeHandler);

        this.input.mouseWheelHandler = this.onMouseWheelHandler;
    }
    private detachEvents(svgElement: SVGSVGElement) {
        this.detachPointerEvents(svgElement);

        RenderHelper.removeEventListener(svgElement, "wheel", this.onMouseWheelHandler);
        RenderHelper.removeEventListener(svgElement, "dblclick", this.onMouseDblClickHandler);
        RenderHelper.removeEventListener(svgElement, "contextmenu", this.onContextMenuHandler);
        RenderHelper.removeEventListener(svgElement, "click", this.onMouseClickHandler);
        RenderHelper.removeEventListener(window, "resize", this.onWindowResizelHandler);
        RenderHelper.removeEventListener(window, "orientationchange", this.onOrientationChangeHandler);
    }

    setPointerPosition(evt: PointerEvent): void {
        this.pointers[evt.pointerId] = {
            clientX: evt.clientX,
            clientY: evt.clientY
        };
    }
    clearPointerPosition(evt: PointerEvent): void {
        delete this.pointers[evt.pointerId];
    }

    onPointerUp(evt: PointerEvent): void {
        this.clearPointerPosition(evt);
        this.onMouseUp(evt);
    }
    onPointerMove(evt: PointerEvent): void {
        if((Browser.TouchUI && !EventUtils.isMousePointer(evt)) || EventUtils.isLeftButtonPressed(evt))
            this.setPointerPosition(evt);
        this.onDocumentMouseMove(evt);
    }
    onPointerCancel(evt: PointerEvent): void {
        this.clearPointerPosition(evt);
    }
    onPointerLeave(evt: PointerEvent): void {
        if(EventUtils.isMousePointer(evt))
            this.onMouseLeave(evt);
        this.clearPointerPosition(evt);
    }

    onPointerDown(evt: PointerEvent): void {
        this.setPointerPosition(evt);
        if(this.getPointerCount() > 2)
            this.pointers = {};

        this.lockMouseMove();
        this.input.lockFocus();
        this.autoScroll.onMouseDown(evt);
        this.lastDownMouseEvent = this.createDiagramMouseEvent(evt);
        raiseEvent(evt, this.lastDownMouseEvent, e => this.events.onMouseDown(e));
        if(this.events.canFinishTextEditing())
            this.input.captureFocus();

        if(EventUtils.isTouchEvent(evt))
            this.processTouchDown(evt);
    }
    onSvgMouseDown(evt: MouseEvent): void {
        EvtUtils.preventEvent(evt);
    }
    onDocumentMouseMove(evt: MouseEvent): void {
        if(this.moveLocked) return;
        this.autoScroll.onMouseMove(evt, () => this.onMouseMoveCore(evt));
        this.onMouseMoveCore(evt);
        Browser.IE && this.lockMouseMove();

        if(EventUtils.isTouchEvent(evt))
            this.processTouchMove(evt);
    }
    onSvgMouseMove(evt: MouseEvent): void {
        EvtUtils.preventEventAndBubble(evt);
    }
    private onMouseMoveCore(evt: MouseEvent) {
        raiseEvent(evt, this.createDiagramMouseEvent(evt), e => this.events.onMouseMove(e));
    }
    onMouseUp(evt: MouseEvent): void {
        this.lockMouseMove();
        const mouseEvent = this.createDiagramMouseEvent(evt);
        raiseEvent(evt, mouseEvent, e => this.events.onMouseUp(e));
        this.autoScroll.onMouseUp(evt);
        if(mouseEvent.source.type !== MouseEventElementType.Undefined)
            this.input.captureFocus(true);
        if(EventUtils.isTouchEvent(evt))
            this.processTouchUp(evt);
    }
    private onMouseEnter(evt: MouseEvent): void {
        this.autoScroll.onMouseEnter(evt);
        raiseEvent(evt, this.createDiagramMouseEvent(evt), e => this.events.onMouseEnter(e));
    }
    private onMouseLeave(evt: MouseEvent): void {
        raiseEvent(evt, this.createDiagramMouseEvent(evt), e => this.events.onMouseLeave(e));
    }
    private onMouseDblClick(evt: MouseEvent): void {
        raiseEvent(evt, this.createDiagramMouseEvent(evt), e => this.events.onDblClick(e));
    }
    private onMouseClick(evt: MouseEvent): void {
        if(!EventUtils.isTouchEvent(evt))
            raiseEvent(evt, this.createActualMouseClickEvent(evt), e => this.events.onClick(e));
        else if(!EventUtils.isMousePointer(evt))
            this.input.captureFocus(); 
    }
    private createActualMouseClickEvent(evt: MouseEvent) : DiagramMouseEvent {
        if(!this.lastDownMouseEvent)
            return this.createDiagramMouseEvent(evt);
        return new DiagramMouseEvent(
            this.lastDownMouseEvent.modifiers,
            this.lastDownMouseEvent.button,
            this.lastDownMouseEvent.offsetPoint.clone(),
            this.lastDownMouseEvent.modelPoint.clone(),
            this.lastDownMouseEvent.source,
            this.createDiagramMouseEventTouches(evt));
    }
    private onContextMenu(evt: MouseEvent) {
        if(!this.contextMenuEnabled) return;
        if(evt.buttons !== 1)
            raiseEvent(evt, this.createDiagramContextMenuEvent(evt), e => this.events.onContextMenu(e));
        this.input.captureFocus();
        return EvtUtils.preventEventAndBubble(evt);
    }
    processTouchDown(evt: MouseEvent): void {
        this.touchDownPoint = this.getTouchPointFromEvent(evt);
        this.resetLongTouch();
        this.longTouchTimer = setTimeout(() => {
            raiseEvent(evt, this.createDiagramMouseEvent(evt), e => this.events.onLongTouch(e));
            this.resetLongTouch();
            this.resetDblClick();
        }, LONG_TOUCH_TIMEOUT);
    }
    processTouchMove(evt: MouseEvent): void {
        const currentTouchPoint = this.getTouchPointFromEvent(evt);
        if(this.touchDownPoint && currentTouchPoint && (Math.abs(this.touchDownPoint.x - currentTouchPoint.x) > RenderManager.touchPositionLimit ||
            Math.abs(this.touchDownPoint.y - currentTouchPoint.y) > RenderManager.touchPositionLimit)) {
            this.resetLongTouch();
            this.resetDblClick();
        }
    }
    getPointers(): Array<Record<string, any>> {
        return Object.keys(this.pointers).map(k => this.pointers[k]);
    }
    getPointerCount(): number {
        return Object.keys(this.pointers).length;
    }
    getTouchPointFromEvent(evt: MouseEvent): Point {
        let touchPosition;
        const touches = evt["touches"];
        if(touches && touches.length > 0)
            touchPosition = new Point(touches[0].clientX, touches[0].clientY);
        else {
            const pointers = this.getPointers();
            if(pointers.length)
                touchPosition = new Point(pointers[0].clientX, pointers[0].clientY);
        }
        return touchPosition;
    }
    processTouchUp(evt: MouseEvent): void {
        if(this.longTouchTimer !== undefined) {
            raiseEvent(evt, this.createDiagramMouseEvent(evt), e => this.events.onClick(e));
            const element = EvtUtils.getEventSource(evt);
            if(this.dblTouchTimer !== undefined && this.lastClickElement === element) {
                raiseEvent(evt, this.createDiagramMouseEvent(evt), e => this.events.onDblClick(e));
                this.resetDblClick();
            }
            else {
                this.resetDblClick();
                this.dblTouchTimer = setTimeout(() => this.dblTouchTimer = undefined, DBL_CLICK_TIMEOUT);
            }
            this.lastClickElement = element;
        }
        this.resetLongTouch();
        this.touchDownPoint = undefined;
    }
    private resetLongTouch() {
        if(this.longTouchTimer !== undefined)
            clearTimeout(this.longTouchTimer);
        this.longTouchTimer = undefined;
    }
    private resetDblClick() {
        if(this.dblTouchTimer !== undefined)
            clearTimeout(this.dblTouchTimer);
        this.dblTouchTimer = undefined;
    }
    onOrientationChange(): void {
        setTimeout(() => this.onWindowResize(), 100);
    }
    onWindowResize(): void {
        let resetTo = { horizontal: false, vertical: false };
        if(this.view.autoZoom !== AutoZoomMode.Disabled) {
            resetTo.horizontal = true;
            resetTo.vertical = true;
        }
        else {
            const oldFitInfo = this.view.checkFitToCanvas();
            const newFitInfo = this.view.checkFitToCanvas(this.scroll.getSize());
            resetTo = { horizontal: oldFitInfo.horizontal !== newFitInfo.horizontal || newFitInfo.horizontal, vertical: oldFitInfo.vertical !== newFitInfo.vertical || newFitInfo.vertical };
        }
        this.view.adjust(resetTo);
    }
    onMouseWheel(evt: WheelEvent): void {
        raiseEvent(evt, this.createDiagramWheelEvent(evt), e => this.events.onMouseWheel(e));
    }

    notifyModelSizeChanged(size: Size, offset?: Offsets): void {
        this.view.notifyModelSizeChanged(size, offset);
    }
    notifyModelRectangleChanged(rectangle: Rectangle): void {
        this.view.notifyModelRectangleChanged(rectangle);
    }
    notifyReadOnlyChanged(readOnly: boolean): void {
        DomUtils.toggleClassName(this.mainElement, READONLY_CSSCLASS, readOnly);
    }
    notifyDragStart(_itemKeys: string[]): void { }
    notifyDragEnd(_itemKeys: string[]): void { }
    notifyDragScrollStart(): void {
        this.autoScroll.onDragScrollStart();
    }
    notifyDragScrollEnd(): void {
        this.autoScroll.onDragScrollEnd();
    }
    notifyToolboxDragStart(evt: MouseEvent): void {
        this.onMouseEnter(evt);
    }
    notifyToolboxDragEnd(evt: MouseEvent): void {
        if(evt && EventUtils.isPointerEvents())
            this.onMouseUp(evt); 
    }
    notifyToolboxDraggingMouseMove(evt: MouseEvent): void {
        this.onDocumentMouseMove(evt); 
    }

    private createDiagramMouseEvent(evt: MouseEvent): DiagramMouseEvent {
        const modifiers = KeyUtils.getKeyModifiers(evt);
        const button = isLeftButtonPressed(evt) ? MouseButton.Left : MouseButton.Right;
        const offsetPoint = this.getOffsetPointByEvent(evt);
        const modelPoint = this.getModelPoint(offsetPoint);
        const isTouchMode = EventUtils.isTouchEvent(evt);
        const eventSource = this.getEventSource(evt, isTouchMode);
        const touches = this.createDiagramMouseEventTouches(evt);
        return new DiagramMouseEvent(modifiers, button, offsetPoint, modelPoint, eventSource, touches, isTouchMode);
    }
    private createDiagramMouseEventTouches(evt: MouseEvent) {
        const touches = [];
        if(evt["touches"])
            for(let i = 0; i < evt["touches"].length; i++) {
                const x = evt["touches"][i].clientX;
                const y = evt["touches"][i].clientY;
                const offsetPoint = this.getOffsetPointByEventPoint(x, y);
                const modelPoint = this.getModelPoint(offsetPoint);
                touches.push(new DiagramMouseEventTouch(offsetPoint, modelPoint));
            }
        else {
            const pointers = this.getPointers();
            for(let i = 0; i < pointers.length; i++) {
                const x = pointers[i].clientX;
                const y = pointers[i].clientY;
                const offsetPoint = this.getOffsetPointByEventPoint(x, y);
                const modelPoint = this.getModelPoint(offsetPoint);
                touches.push(new DiagramMouseEventTouch(offsetPoint, modelPoint));
            }
        }

        return touches;
    }
    private createDiagramContextMenuEvent(evt: MouseEvent) {
        const modifiers = KeyUtils.getKeyModifiers(evt);
        const eventPoint = new Point(evt.pageX, evt.pageY);
        const offsetPoint = this.getOffsetPointByEvent(evt);
        const modelPoint = this.getModelPoint(offsetPoint);
        return new DiagramContextMenuEvent(modifiers, eventPoint, modelPoint);
    }
    private createDiagramWheelEvent(evt: WheelEvent) {
        const modifiers = KeyUtils.getKeyModifiers(evt);
        const offsetPoint = this.getOffsetPointByEvent(evt);
        const modelPoint = this.view.getModelPoint(offsetPoint);
        const eventSource = this.getEventSource(evt);
        const deltaX = evt.deltaX || (evt["originalEvent"] && evt["originalEvent"].deltaX);
        const deltaY = evt.deltaY || (evt["originalEvent"] && evt["originalEvent"].deltaY);
        return new DiagramWheelEvent(modifiers, deltaX, deltaY, offsetPoint, modelPoint, eventSource);
    }

    private getEventSource(evt: MouseEvent, findByPosition?: boolean): MouseEventSource {
        let element = findByPosition ? EvtUtils.getEventSourceByPosition(evt) : EvtUtils.getEventSource(evt);
        if(this.isDiagramControl(element))
            while(element && !this.isDocumentContainer(element)) {
                const src = RenderUtils.getElementEventData(element);
                if(src !== undefined)
                    return src;
                if(this.input.isTextInputElement(element))
                    return new MouseEventSource(MouseEventElementType.Document);
                element = <HTMLElement>element.parentNode;
            }
        const src = new MouseEventSource(MouseEventElementType.Undefined);
        if(element && this.isDocumentContainer(element))
            src.type = MouseEventElementType.Background;
        return src;
    }
    private isDiagramControl(element: HTMLElement): boolean {
        while(element) {
            if(this.isDocumentContainer(element))
                return true;
            element = <HTMLElement>element.parentNode;
        }
        return false;
    }
    private isDocumentContainer(element: HTMLElement): boolean {
        return element === this.mainElement;
    }
    private lockMouseMove() {
        this.moveLocked = true;

        this.lockMouseMoveTimer = setTimeout(() => {
            this.moveLocked = false;
            this.lockMouseMoveTimer = -1;
        }, 10); 
    }
    private killLockMouseMoveTimer() {
        if(this.lockMouseMoveTimer !== -1) {
            clearTimeout(this.lockMouseMoveTimer);
            this.lockMouseMoveTimer = -1;
        }
    }
    private clearLastMouseDownEvent() {
        this.lastDownMouseEvent = undefined;
    }
    protected getModelPoint(offsetPoint: Point): Point {
        return this.view.getModelPoint(offsetPoint);
    }
    protected getOffsetPointByEvent(evt): Point {
        const clientX: number = EvtUtils.getEventX(evt);
        const clientY: number = EvtUtils.getEventY(evt);
        return this.getOffsetPointByEventPoint(clientX, clientY);
    }
    protected getOffsetPointByEventPoint(clientX: number, clientY: number): Point {
        const scrollContainer = this.scroll.getScrollContainer();
        const containerX = DomUtils.getAbsolutePositionX(scrollContainer);
        const containerY = DomUtils.getAbsolutePositionY(scrollContainer);
        return new Point(clientX - containerX, clientY - containerY);
    }
    getModelPointByEventPoint(clientX: number, clientY: number): Point {
        const offsetPoint = this.getOffsetPointByEventPoint(clientX, clientY);
        return this.view.getModelPoint(offsetPoint);
    }
    getEventPointByModelPoint(point: Point): Point {
        const pos = this.view.getAbsolutePoint(point);
        const scrollContainer = this.scroll.getScrollContainer();
        return new Point(
            DomUtils.getAbsolutePositionX(scrollContainer) + pos.x,
            DomUtils.getAbsolutePositionY(scrollContainer) + pos.y
        );
    }
}

function isLeftButtonPressed(evt: MouseEvent): boolean {
    return !Browser.MSTouchUI ? EventUtils.isLeftButtonPressed(evt) : evt.button !== 2;
}
