import {AbstractTool} from '@int/geotoolkit/controls/tools/AbstractTool';
import {Dimension} from '@int/geotoolkit/util/Dimension';
import {Rectangle} from '@int/geotoolkit/scene/shapes/Rectangle';
import {getAbsolutePosition, createCanvasElement} from '@int/geotoolkit/dom';
import '../styles/magnifier.css';

const addEventListener = function (target, eventName, eventHandler) {
    AbstractTool.getNativeEventName(eventName).forEach((event) => {
        target.addEventListener(event, eventHandler, true);
    });
};

const removeEventListener = function (target, eventName, eventHandler) {
    AbstractTool.getNativeEventName(eventName).forEach((event) => {
        target.removeEventListener(event, eventHandler, true);
    });
};

const getClientPosition = function (eventArgs) {
    let clientX = 0, clientY = 0;
    if (eventArgs.touches != null && eventArgs.touches.length > 0) {
        // touch events
        clientX = eventArgs.touches[0].screenX;
        clientY = eventArgs.touches[0].screenY;
    } else {
        // click events
        clientX = eventArgs.screenX;
        clientY = eventArgs.screenY;
    }
    return {
        x: clientX,
        y: clientY
    };
};

export class Magnifier {
    constructor () {
        this._visible = false;
        this._canvas = null;
        this._size = 20;
        this._x = 0;
        this._y = 0;
        this._lastPickTime = 0;

        this.initializeUI();
    }

    initializeUI () {
        this._magnifierDialog = document.createElement('div');
        this._magnifierDialog.classList.add('magnifier-dialog');
        this._magnifierDialog.style.display = 'none';
        this._magnifierDialog.style.zIndex = 1003;

        document.body.appendChild(this._magnifierDialog);

        this._canvas = createCanvasElement(256, 256);
        this._canvas.id = 'magnifier-canvas';
        this._magnifierDialog.appendChild(this._canvas);

        this._onWindowMouseMove = this.onWindowMouseMove.bind(this);
        this._onWindowMouseUp = this.onWindowMouseUp.bind(this);

        const onMouseDown = (eventArgs) => {
            this.start(eventArgs);
        };
        addEventListener(this._magnifierDialog, 'pointerdown', onMouseDown);
        addEventListener(this._canvas, 'pointerdown', onMouseDown);
    }

    start (eventArgs) {
        if (this._position != null) return;
        eventArgs.stopPropagation();
        eventArgs.cancelBubble = true;

        addEventListener(window, 'pointermove', this._onWindowMouseMove);
        addEventListener(window, 'pointerup', this._onWindowMouseUp);

        this._position = getAbsolutePosition(this._magnifierDialog);
        this._mousePosition = getClientPosition(eventArgs);
    }

    stop () {
        removeEventListener(window, 'pointermove', this._onWindowMouseMove);
        removeEventListener(window, 'pointerup', this._onWindowMouseUp);

        this._position = null;
        this._mousePosition = null;
    }

    onWindowMouseMove (eventArgs) {
        if (eventArgs.buttons === 0) { // bug in IE11: pointer up events can't be caught outside iframe
            this.onWindowMouseUp();
            return;
        }
        eventArgs.preventDefault();
        eventArgs.stopPropagation();
        eventArgs.cancelBubble = true;

        const mousePosition = getClientPosition(eventArgs);
        this._position.x += mousePosition.x - this._mousePosition.x;
        this._position.y += mousePosition.y - this._mousePosition.y;

        this._magnifierDialog.style.left = this._position.x + 'px';
        this._magnifierDialog.style.top = this._position.y + 'px';
        this._mousePosition = mousePosition;
    }

    onWindowMouseUp (eventArgs) {
        this.stop();
    }

    getVisible () {
        return this._visible;
    }

    setVisible (visible) {
        if (this._visible === visible) return this;
        this._visible = visible;

        if (this._visible === true) {
            this._magnifierDialog.style.display = '';
        } else {
            this._magnifierDialog.style.display = 'none';
        }
        this._magnifierFrame.setVisible(this._visible);
        return this;
    }

    setPipeline (pipeline, manipulatorLayer) {
        this._pipeline = pipeline;
        if (this._manipulatorLayer != null) return this;

        this._manipulatorLayer = manipulatorLayer;
        this._magnifierFrame = new Rectangle(0, 0, 0, 0)
            .setVisible(false)
            .setLineStyle({
                'pixelsnapmode': true
            })
            .setFillStyle('rgba(255,0,0,0.1)');
        this._manipulatorLayer.addChild(this._magnifierFrame);
        return this;
    }

    setFrame (x, y, size) {
        const visualLayer = this._manipulatorLayer;
        if (!(size instanceof Dimension)) {
            size = new Dimension(size, size);
        }

        size = visualLayer.getSceneTransform().inverseTransformDimension(size, size);

        this._magnifierFrame.setRect(x - size.width / 2, y - size.height / 2, x + size.width / 2, y + size.height / 2)
            .setVisible(true);
    }

    pickFrame (x, y) {
        if (!Number.isFinite(x) || !Number.isFinite(y)) {
            this._canvas.getContext('2d').clearRect(0, 0, this._canvas.width, this._canvas.height);
            return;
        }
        const pickTime = Date.now();
        this._lastPickTime = pickTime;
        this.setFrame(x, y, this._size);
        this._pipeline.exportToImage(this._magnifierFrame.getBounds(), this._canvas, null, 0, 0, null,
            () => (pickTime === this._lastPickTime));
    }

    dispose () {
        if (this._position != null) {
            this.stop();
        }
        this._magnifierDialog.removeChild(this._canvas);
        this._canvas = null;
        document.body.removeChild(this._magnifierDialog);
        this._magnifierDialog.classList.remove('magnifier-dialog');
        this._magnifierDialog = null;
    }
}
